Files
safetynet/apps/network-broker/native

Native Windows service host

This component uses Windows SCM, named pipes and Job objects around the stock Node broker. It does not rebuild Electron/Chromium or modify installed Chrome. It has not been installed or proven as the production network boundary.

Run node scripts/build-windows-service-host.mjs --self-test on Windows with Visual Studio C++ Build Tools and SDK 10.0.26100.0. The builder uses explicit compiler/include/library paths, /W4 /WX, static runtime, CFG, ASLR and DEP. Output and fixed metadata evidence remain under ignored artifacts/native-service-host. It changes no execution policy, service, firewall, DNS setting or user profile. The builder accepts --self-test or --compile-only. Compile-only never launches the executable and writes separate evidence. It bakes a disposable public key, retains private test keys only in memory, and signs synthetic inventory fixtures after compilation. It never creates a production release key or registers a service.

The actual negative tests verify: missing SCM service identity, unauthorized client image, oversized frame, wrong file hash, second pipe instance denied by its DACL, and a child plus grandchild retained in a non-breakaway kill-on-close Job and both observed terminal after closing the Job. These are meaningful native component tests; they do not prove an installed SCM or WFP deployment.

The fixed bridge is \\.\pipe\SafetyNet.Broker.v1, local-only, one authenticated browser connection at a time. An enabled SCM service SID, protected code and independently pinned deployment material are required before broker launch. The client is the protected installed helper launched by the protected installed SafetyNet.exe. Both must have matching medium/high, non-AppContainer user tokens and sessions. Its process token and impersonated pipe token must agree on user SID and session. The process handle remains retained and monitored; client exit terminates the broker Job. The ACL excludes FILE_CREATE_PIPE_INSTANCE from employee rights. No trust arises from a pipe name or claimed PID in JSON.

Frames are uint32 little-endian length followed by UTF-8 JSON. The first parent-to-child frame is bounded to 2 MiB; subsequent frames and child responses are 64 KiB. The constant child receives only inherited stdin/stdout handles and --service-stdio; stderr goes to NUL. Seven Windows profile/runtime variables and one fixed OS-derived SAFETYNET_SERVICE_CLIENT_SID are passed. No caller environment supplies that SID. The Node adapter must validate the existing v1 generation, monotonic sequence, typed audit/certificate receipts and pinned service deployment authority. Framing does not authenticate policy or allow a caller to replace its public key, device identity or version floor.

The --client-stdio mode connects once and authenticates the actual connected handle's retained server process against signed release hashes, the exact installed path and an enabled SCM service SID. It never probes and reconnects. --trust-snapshot requires a service-token helper and a retained signed Node parent. It returns bounded framed public anchor/SID/registry-floor metadata, without accepting a registry path or trust key from its caller.

The fixed child entry is resources/broker/apps/network-broker/service-entry.cjs. The prepared --topology-snapshot helper uses the compiled broker port 43120, fixed installed browser/broker paths and the real SCM service SID. Service-token, signed Node-parent and signed user-assignment checks precede its read-only BFE enumeration. It verifies all eight owned filter keys, actions, conditions, weights and service descriptor bytes, checks equal-priority collisions and BFE state, and reports bounded metadata. It never accepts a plan, port or visibility flag from IPC. The new topology source compiled, but its latest self-test launch failed with UNKNOWN:spawn; compile-only evidence is separate from earlier successful native tests. This helper has no installed execution proof yet.

Windows enumerators omit unreadable objects and notifications are asynchronous and unreliable. Therefore the helper reports allObjectsReadable:false even at successful EOF. Production admission requires an independently reviewed, protected deployment prerequisite; token elevation or an ordinary JSON flag cannot establish it. Ordinary-user enforcement assumes trusted administrators; user-mode monitoring cannot promise zero packets during hostile privileged kernel/BFE changes. See Microsoft's access control and notification contract.

The fixed 64-bit HKLM key SOFTWARE\SafetyNet\Trust contains one canonical AcceptanceState REG_BINARY value, including release/deployment digests and sorted per-user policy floors. Missing or corrupt state fails closed. The snapshot exposes {version, authenticatedSid, releaseAnchor, acceptanceState}. --commit-acceptance accepts only {expectedPreviousSHA, candidateState}, checks a full monotonic transition under an authenticated fixed mutex, writes one value, flushes and verifies readback before returning {persisted:true,stateSha256}. State bytes are capped at 2 MiB minus 1024 bytes; all stdio frames have a single bounded deadline. The trusted Node gate must verify the actual signed policy before requesting a commit. Policy digests hash the original base64url payload string; manifest digests hash canonical decoded JSON bytes.

The prepared --management-credential command accepts exactly device ID, canonical HTTPS origin, policy version, policy-key DER SHA256 and the accepted policy-payload digest. It binds them to the native service-minted user SID, signed v2 deployment and exact protected acceptance record. Its private reply contains only the same fields and the token, bounded to 8 KiB. The separate fixed key SOFTWARE\SafetyNet\ManagementCredentials requires a protected DACL owned by SYSTEM/administrators, granting access only to them and the exact service SID. Encrypted records use LocalMachine DPAPI with canonical metadata as entropy; this is Windows encryption, not a post-quantum storage claim.

Each read first validates the exact current accepted policy request. It then selects the unique highest stored version at or below that request for the same SID, device, origin and policy key. Same-version digest conflicts, ambiguous duplicates and future versions fail closed. Decryption uses the record's own original metadata; the reply echoes the verified current request. Advancing a policy therefore does not require re-encrypting an otherwise valid credential. Provisioning replaces all records for that stable identity and preserves other identities, preventing a new record for every policy revision.

Internal --provision-management-credential additionally requires the previous encrypted-store SHA256 and token. It uses the same authenticated service role, serialized mutex, bounded canonical store (256 records/1 MiB), CAS, flush and readback before returning a receipt. It accepts no registry path, user SID or key selector. Neither command creates keys or supplies an initial empty store. No renderer/enrollment provisioning endpoint is exposed. Initial protected provisioning and actual service-token DPAPI/registry execution remain unproven; the latest native source has compile-only evidence, not execution evidence.

The mutex uses an OWNER_RIGHTS read-control ACE rather than assuming the service SID can own objects. Actual native tests show owner WRITE_DAC denial while synchronization and modification remain allowed. Commit source is prepared, but no registry write, installed service positive or durable commit has been performed. Current uninstalled snapshot/commit/client tests require exit 5.

signed-manifest.h verifies canonical manifests using Windows CNG P-256/SHA256 and fixed 64-byte IEEE-P1363 signatures. The baked vendor key verifies release inventory; its delegated customer key verifies deployment and user assignment. This is a classical release signature, not a post-quantum signature. Actual native tests verify positive delegation and reject tampering, the wrong customer key, expiry and equal-sequence digest mismatch. A larger signed inventory tests real envelope parsing. These fixture tests do not attest installed resource ACLs.

Before deployment, implement and independently verify these remaining gates:

  • Actual vendor signing/provisioning and complete final inventory, including every broker, inspector, model and browser resource. The external signature avoids circular browser/host hashes; synthetic fixtures are not a release.
  • Installed positive client/server authentication and lifecycle tests; current native negatives cannot prove a correctly provisioned SCM service.
  • Protected initial acceptance-state provisioning and an actual service-token CAS/flush/readback proof before listen. Prepared source and uninstalled negative tests do not establish durable installed behavior.
  • Real SCM provisioning under a least-privileged service identity, protected resource/state ACLs, actual service-start/stop and bounded I/O timeout tests.
  • Per-user signed assignment binding and typed management/inspector/OTLP RPC. Interactive DPAPI audit persistence stays in the authenticated user parent.
  • Transactional WFP outbound filters and receiver service-descriptor soft-permit plus lower block in the same owned sublayer, preserving corporate veto.
  • Native readback and physical IPv4/IPv6/UDP/DNS/port-takeover tests, including another instance of the same stock Node executable and service crashes.
  • Effective WFP hard-permit topology validation and BFE change monitoring; a low-priority sublayer or a static rule inventory cannot prove enforcement. Browser inbound-first IPv4/IPv6 TCP/UDP denies need physical receiver tests.

Until those gates pass, retain existing browser quarantine and enforcement.