Add CI validation helpers with explicit signature selection and owned results

This commit is contained in:
nefarius committed 2026-10-26 23:45:12 +01:00
1 parent 28f284be8f
commit 1f4d62ae95
13 files changed
+596 -3

No files matched your search

+5 -2
View File
@@ -197,8 +197,11 @@ statuses and signing time. Do not interpret status success alone as selection
of the company certificate. First successful does not necessarily mean first
listed: CI may reject the primary and continue.
`0x80` has additional policy and fallback effects. It has not been published as
a supported Domito constant or a production default. The companion research
`0x80` has additional policy and fallback effects. Domito exposes it only as
`DOMITO_CI_VALIDATE_FLAG_FIRST_SUCCESS_EXPERIMENTAL` and through the opt-in
`DomitoCiSignatureSelectionFirstSuccessfulExperimental` mode in
`DomitoValidateFileObjectEx`. It is not a production default or a stable signer
index. See the [validation helper contracts](ci-validation-helpers.md). The companion research
`SIGNATURE-SELECTION.md` provides the full static evidence and an alternative
primary-signature path using `CiCheckSignedFile`.
+116
View File
@@ -0,0 +1,116 @@
# CI validation helpers
These additive APIs are declared in `Domito.h` for Windows 10 compile targets.
They reuse CI for cryptographic verification. They do not implement RSA/ECDSA,
certificate-chain building or timestamp-signature verification themselves.
Raw CI declarations and existing digest/legacy helpers are unchanged.
## Result lifetime
Zero-initialize `DOMITO_CI_VALIDATION_RESULT` before first use. Each validation
call frees its previous signer/timestamp policies before reuse and resets the
result. Do not pass uninitialized storage or copy an owning result as a second
owner. On every exit, including a failed call, release it with
`DomitoFreeCiValidationResult`. The destructor accepts NULL and empty results.
All three APIs require PASSIVE_LEVEL. Referenced input objects/buffers must stay
valid and file bytes must stay stable throughout validation and consumption.
On success, `Hash`/`HashSize`/`HashAlgId` describe a known SHA-1/256/384/512 file
digest with a consistent length. Unused digest bytes are zero. This is not a
certificate thumbprint. On failure, digest bytes/length/algorithm and signing
time are zero; policy objects may retain owned diagnostic allocations. Input
flags/level forwarded to the file-object export remain recorded if it was called.
Those fields are not CI's final internally normalized policy flags.
Check the returned NTSTATUS first. Policy verification status, policy bits,
certificate identity, timestamps and the application's intended trust policy
remain separate observations/checks. Returning STATUS_SUCCESS does not pin a
company signer, reproduce WinVerifyTrust policy or prove driver-load acceptance.
No helper traverses the private chain layout or matches a certificate for you.
## File-object path
`DomitoValidateFileObjectEx` takes a referenced FILE_OBJECT, caller-selected
validation flags, a byte-sized requested signing level and a selection mode.
It supplies fresh 64-byte hash capacity and initialized policy/time outputs on
every call. It does not read, duplicate or acquire ownership of the file object.
- `DomitoCiSignatureSelectionNative` forwards the caller's flags unchanged.
This is native policy, not a guarantee of preferred-level selection: the
caller's other flags may already change the search/exit path.
- `DomitoCiSignatureSelectionFirstSuccessfulExperimental` additionally sets
`DOMITO_CI_VALIDATE_FLAG_FIRST_SUCCESS_EXPERIMENTAL` (0x80). On examined x64
builds this changes a successful-candidate early-exit predicate. It also
affects other policy/fallback paths. This explicit mode returns
STATUS_NOT_SUPPORTED on non-x64 builds. No runtime behavior on ARM64 is implied.
Neither mode automatically adds `0x20`. Broadening accepted roots is a separate
caller decision. Neither mode selects an arbitrary signer index or guarantees
that signature zero succeeds. A rejected primary may still lead to another signer.
```c
DOMITO_CI_VALIDATION_RESULT result = {0};
NTSTATUS status = DomitoValidateFileObjectEx(
fileObject,
DOMITO_CI_VALIDATE_FLAG_BROADEN_ROOT_ACCEPTANCE,
0,
DomitoCiSignatureSelectionFirstSuccessfulExperimental,
&result);
if (NT_SUCCESS(status)) {
// Apply your expected-certificate/trust policy to result.PolicyInfo.
// result.SigningTime belongs to that selected verification result.
}
DomitoFreeCiValidationResult(&result);
```
The native kernel experiment on the supplied dual-SHA256 BthPS3 fixture returned
Microsoft for flags 0x20 and Nefarius for flags 0xA0 on a Windows 11 22631 x64
TESTSIGNING-enabled machine. It does not establish all older builds, TESTSIGNING-off
behavior or every side effect of 0x80. See [the flag evidence](ci-validate-file-object.md).
Do not rename this mode "require our company" or "select primary signature".
## Embedded PE-buffer path
`DomitoValidatePortableExecutableEmbeddedSignature` accepts a stable PE buffer
and an explicit CALG_SHA1/CALG_SHA_256/CALG_SHA_384/CALG_SHA_512. It calls
`DomitoCalculatePortableExecutableDigestEx` and supplies the resulting digest
and original first WIN_CERTIFICATE to `CiCheckSignedFile`. The digest allocation
is always freed, and only copied digest bytes/owned CI policies remain in the
result. Unsupported algorithm/layout and CI errors propagate without trying a
different algorithm or a catalog. An absent certificate returns
STATUS_INVALID_IMAGE_HASH; the helper cannot turn an unsigned file into a
successful catalog verification.
```c
DOMITO_CI_VALIDATION_RESULT result = {0};
NTSTATUS status = DomitoValidatePortableExecutableEmbeddedSignature(
stablePeBytes, peSize, CALG_SHA_256, &result);
if (NT_SUCCESS(status)) {
// Enforce the expected certificate and intended trust policy here.
}
DomitoFreeCiValidationResult(&result);
```
This uses the explicit digest helper's [supported PE layout subset](authenticode-digests.md):
bounded headers, contiguous nonoverlapping raw sections in file order and an EOF
certificate table. It is not an arbitrary PE/SIP parser. CI can select nested
signatures according to its algorithm rules, so the helper is deliberately named
**embedded**, not **force primary**. The matching SHA-256 request selected the
company primary in the tested BthPS3 layout; another layout may select another
signer or fail. General enumeration remains separate work.
## Support and validation
The functions are C-compatible and compile for x64/ARM64. The experimental
file-object mode is deliberately gated to x64; native forwarding/embedded code
compilation does not certify every architecture/build's CI runtime ABI. CI
imports are static and must exist on the target OS, as described in the
[availability matrix](ci-api-compatibility.md). Structure/chain interpretation
remains subject to the [mincrypt contracts](mincrypt-contracts.md).
The new host suite checks production code forwarding, hash invariants, failures,
partial allocations, cleanup, result reuse and the non-x64 conditional branch.
The existing digest suite independently compares Windows SIP and Crypt32 results.
Kernel compilation covers the full library, public C clients and layout checks.
CodeIntegrity-RE probe v2 now calls the new APIs; a further kernel run is needed
to validate these wrappers rather than merely their previously tested raw calls.
+8 -1
View File
@@ -6,6 +6,13 @@ it should not be read as describing the corrected source unchanged.
## Implemented with host tests or compilation
- Additive CI result/selection helpers now package native file-object validation,
opt-in x64 first-success selection and explicit embedded verification, with
shared policy/digest cleanup. Existing APIs retain their behavior. See the
[helper contracts](ci-validation-helpers.md). Host failure/forwarding tests and
x64/ARM64 builds cover the wrappers; WPP probe v2 consumes them and awaits a
returned kernel trace. The previous v1 trace establishes the raw native routes.
- **S01–S04:** subtraction-safe PE ranges, no fixed 4 MiB copy, bounded certificate
intervals, deterministic failure outputs, and digest cleanup. Compatibility
and explicit paths share the bounded hashing core.
@@ -95,7 +102,7 @@ and compilation do not close the remaining work:
## Checks
Run the three host runners in `tests/README.md`. Kernel compilation also covers
Run the four host runners in `tests/README.md`. Kernel compilation also covers
the default modern x64/ARM64 structures, the pre-Windows-10 x64 compile variant,
and 14 assertions in the 26100 opt-in header plus 12 historical-view assertions. Those are build checks,
not a claim that historical or ARM64 CI runtime ABI has been verified.
+4
View File
@@ -158,6 +158,10 @@ later conversion failure. It is not owned by either policy object.
Evidence: `MincryptFreePolicyInfo`, `MincryptCopyPolicyInfo`,
`I_MincryptAddChainInfo`, `CiVerifyHashInCatalog`, and `CiGetCertPublisherName`.
The additive [validation helpers](ci-validation-helpers.md) package safe result
ownership, explicit file-object selection and embedded CI verification. They do
not change raw CI declarations or existing legacy-helper behavior.
## Multiple signatures and selected results
`CiValidateFileObject` returns one selected signature result and its timestamp