diff --git a/implementations/main.cpp b/implementations/main.cpp index f8df4b6..4671f44 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -57,6 +57,7 @@ bool g_auth = false; int g_frames = 40; int g_frameGapMs = 500; std::string g_logDir = "/var/log/fingerprintd"; +int g_rescan = -1; // -1 = leave the config's value alone std::uint32_t g_gid = 0; std::string g_taPath = "/lib/firmware/focal64.mbn"; std::string g_cfgPath = "/lib/firmware/fingerprintd.json"; @@ -944,6 +945,29 @@ int Probe() { } std::string json((std::istreambuf_iterator(cf)), std::istreambuf_iterator()); + + // common.max_authentication_rescan_times bounds how many frames the + // matcher may answer "not identified yet" before it has to produce a + // verdict. At the stock default a whole run can end undecided, which is + // the right shipping behaviour and useless as a measurement: a + // wrong-finger control that never reaches a verdict has not demonstrated + // a rejection. Setting it to 0 forces every frame terminal. + // + // MEASUREMENT ONLY. A rate measured this way is a per-frame figure taken + // with the retry mechanism disabled and is not a shipping reject rate. + if (g_rescan >= 0) { + auto at = json.find("\"common\":{"); + if (at == std::string::npos) at = json.find("\"common\": {"); + if (at == std::string::npos) { + std::println(std::cerr, "config has no \"common\" object to patch"); + return 1; + } + auto brace = json.find('{', at); + json.insert(brace + 1, + std::format("\"max_authentication_rescan_times\":{},", g_rescan)); + std::println("forcing max_authentication_rescan_times={} (MEASUREMENT ONLY)", + g_rescan); + } // The trustlet wants the terminating NUL counted. std::vector cfg(json.size() + 1, std::byte{0}); for (std::size_t i = 0; i < json.size(); i++) @@ -1100,7 +1124,13 @@ int Probe() { // buffer cannot tell "the matcher never ran" from "the matcher // ran and rejected the finger" -- the failure path writes zero // there too, so zero is ambiguous and 0xAAAAAAAA is not. - ta::PoisonFid(std::span(evbuf).subspan(ta::ResponsePayloadOff)); + // PoisonFid already writes at RespFidOff within the PAYLOAD. + // Handing it a span that is itself already offset by the + // payload offset double-counts and poisons payload+0x20, so + // the real fid field stays zero -- and a released finger then + // classifies as a REJECTION, inventing failures that never + // happened. + ta::PoisonFid(evbuf); auto r = SendCommand(app, ta::Cmd::ReportEvent, evbuf); if (!r.invoked) continue; @@ -1162,6 +1192,7 @@ int main(int argc, char** argv) { if (a == "--auth") { g_auth = true; g_listeners = true; } if (a.starts_with("--frames=")) g_frames = std::stoi(std::string(a.substr(9))); if (a.starts_with("--log-dir=")) g_logDir = a.substr(10); + if (a.starts_with("--rescan=")) g_rescan = std::stoi(std::string(a.substr(9))); if (a.starts_with("--sfs-root=")) g_sfsRoot = a.substr(11); if (a.starts_with("--gid=")) g_gid = static_cast( std::stoul(std::string(a.substr(6)))); diff --git a/tests/Ta/main.cpp b/tests/Ta/main.cpp index 4914f6e..65dae76 100644 --- a/tests/Ta/main.cpp +++ b/tests/Ta/main.cpp @@ -276,6 +276,19 @@ int main() { Check(Get32(payload, RespFidOff) == FidPoison, "poison written at +0x10"); Check(Classify(0, Get32(payload, RespFidOff)) == Verdict::MatcherNeverRan, "an untouched poisoned payload classifies as never-ran"); + + // PoisonFid takes the PAYLOAD and offsets internally. Handing it a + // span already offset by ResponsePayloadOff double-counts and poisons + // payload+0x20, leaving the real fid field zero -- which makes every + // released finger read as a rejection. That shipped once. + Check(RespFidOff == ResponsePayloadOff, + "the two offsets are equal, which is exactly why double-applying is silent"); + std::vector wrong(64); + PoisonFid(std::span(wrong).subspan(ResponsePayloadOff)); + Check(Get32(wrong, RespFidOff) != FidPoison, + "double-offsetting leaves the fid field unpoisoned"); + Check(Classify(0, Get32(wrong, RespFidOff)) == Verdict::Rejected, + "and an unpoisoned release is then misread as a rejection"); } // ---- Init chain