Extend identity confirmation for unsigned slices#953
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughVerifyingHasher facade now supports unsigned Mach-O binaries by splitting verification into signed and unsigned paths. The Expected contract captures filesystem identity (stat tuple) and optionally signed expectations. Unsigned verification validates the stat tuple before reading; signed verification uses the signed_check path. Tests, fixture, BUILD, and CLI were updated. ChangesUnsigned Verification Support
Sequence DiagramsequenceDiagram
participant App
participant Run as VerifyingHasher::Run
participant Stat as fstat
participant Core as VerifyingHasherCore
App->>Run: Run(fd, exp)
alt unsigned (exp.signed_check unset)
Run->>Stat: validate (dev, ino, size, mtime)
alt stat match
Run->>Core: create reader (size=exp.stat.size), Run()
Core-->>Run: core_status, CDHash
alt core_status == kNoSignature
Run-->>App: kMatchUnsigned + sha256
else
Run-->>App: kError
end
else stat mismatch
Run-->>App: kError (no sha256)
end
else signed (exp.signed_check set)
Run->>Core: create reader (size=exp.stat.size), Run()
Core-->>Run: core_status, CDHash
alt cdhash matches signed_check
Run-->>App: kMatch + sha256
else
Run-->>App: kError
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
…ty confirmation Adds a new identity-confirmation mode to the VerifyingHasher facade for Mach-O slices that do not carry an embedded code signature. Confirmation is via a (dev, ino, size, mtime) stat tuple captured at AUTH EXEC and compared against fstat(fd) on the same fd inside the facade. ### Public surface Expected becomes a flat struct: a required StatTuple stat and an optional Signed sub-struct. When signed_check is engaged, Run() verifies the slice via cdhash / signing_id / team_id (existing behavior). When signed_check is nullopt, Run() requires the slice to have no embedded signature and confirms identity via the stat tuple, compared across all four fields including nanosecond mtime. The new Status::kMatchUnsigned reports this success outcome. | Type | Change | | --- | --- | | `Expected::Signed` | Nested in `Expected` and wrapped in `optional<Signed> signed_check` | | `Expected::stat` | New required `StatTuple` | | `StatTuple` | New: `dev`, `ino`, `size`, `mtime` (nanosecond precision) | | `Status::kMatchUnsigned` | New variant for stat-confirmed unsigned success | ### Behavior The facade no longer calls fstat() on the path that consumes signed_check; the file-reader size hint comes from exp.stat.size, which callers supply from the ES event. On the path that does not consume signed_check, the facade fstats once at entry and returns kError with sha256 nullopt if the stat tuple does not match — no file read in that case. kMatchUnsigned requires both: the fstat-derived stat tuple matches Expected.stat across all four fields, AND Core reports kNoSignature for the slice. The two requirements are independent and both load-bearing. ### Testing Adds an unsigned thin arm64 fixture (testdata/hw_unsigned) and 11 new facade tests covering the happy path, per-field stat-mismatch (5 fields including nanosecond mtime), cross-kind anomalies in both directions, kNoSignature-gating against non-Mach-O input, fstat-failure handling, and baseline preservation of the empty-signed_check behavior. Existing tests migrated to the new Expected shape. The OneOffs exerciser gains a -u flag and the smoke test gains a case exercising it end-to-end against a codesign-stripped binary.
2f8068d to
921e236
Compare
Adds a new identity-confirmation mode to the VerifyingHasher facade for Mach-O slices that do not carry an embedded code signature. Confirmation is via a (dev, ino, size, mtime) stat tuple captured at AUTH EXEC and compared against fstat(fd) on the same fd inside the facade.
Public surface
Expected becomes a flat struct: a required StatTuple stat and an optional Signed sub-struct. When signed_check is engaged, Run() verifies the slice via cdhash / signing_id / team_id (existing behavior). When signed_check is nullopt, Run() requires the slice to have no embedded signature and confirms identity via the stat tuple, compared across all four fields including nanosecond mtime. The new Status::kMatchUnsigned reports this success outcome.
Expected::SignedExpectedand wrapped inoptional<Signed> signed_checkStatTupledev,ino,size,mtime(nanosecond precision)Behavior
The facade no longer calls fstat() on the path that consumes signed_check; the file-reader size hint comes from exp.stat.size, which callers supply from the ES event. On the path that does not consume signed_check, the facade fstats once at entry and returns kError with sha256 nullopt if the stat tuple does not match — no file read in that case.
kMatchUnsigned requires both: the fstat-derived stat tuple matches Expected.stat across all four fields, AND Core reports kNoSignature for the slice. The two requirements are independent and both load-bearing.
Testing
Adds an unsigned thin arm64 fixture (testdata/hw_unsigned) and 11 new facade tests covering the happy path, per-field stat-mismatch (5 fields including nanosecond mtime), cross-kind anomalies in both directions, kNoSignature-gating against non-Mach-O input, fstat-failure handling, and baseline preservation of the empty-signed_check behavior. Existing tests migrated to the new Expected shape. The OneOffs exerciser gains a -u flag and the smoke test gains a case exercising it end-to-end against a codesign-stripped binary.