Pular para o conteúdo principal

Security

What protects what on the platform, where each secret lives, and what an operator has to keep.

The founding claim, and its one exception​

The private key of a Workstation Certificate never leaves the workstation. The Workstation Service is the only path to the certificates on a machine, the key only ever sees a hash, and signing with an A3 certificate happens on the hardware that holds it. Nothing on the Server can sign with a Workstation Certificate, and nothing in the product exports one.

The Certificate Vault is the deliberate exception, off by default. With Vault:Enabled=true an Administrator can import an A1 certificate, key and all, and the Server signs with it. That trades custody for convenience: a key at rest in the platform's own store rather than on a card that cannot give it up. Which custody a certificate uses is an operator's and an auditor's concern; a client application is never told.

PIN handling​

A PIN is what authenticates a holder to their token so a signature can be made. Since Server 1.13.0 and Workstation 1.5.0 (workstation protocol 5), the PIN a client application sends goes no further than the Server. Four rules govern it:

  • The PIN is entered locally. The holder types it at the Tray App on the machine the token is plugged into, in the console session only. The Workstation Service caches it, encrypted, in the PIN Cache, keyed per certificate, with a sliding idle TTL (8 h by default). For an A1 Certificate the machine keeps a verifier of an A1 PIN instead (below).

  • A match is a proof the Server checks. At POST /v1/sessions the Server challenges every candidate machine at once with a fresh random nonce. Each answers with a PIN Proof, an HMAC over the nonce keyed by the PIN it has cached (or by its A1 PIN's verifier), or says it has nothing to prove with. The Server computes the expected proof from the PIN the client sent and decides. The machine a session binds to is told so, and a machine whose Tray prompt was answered is told the verdict on that entry; that is all a machine learns. A machine never receives the PIN, and a wrong value from the network never reaches the token or burns its retry counter. Only PINs the holder typed reach the hardware. A Server from 1.15.0 refuses a machine below protocol 5 at the handshake.

  • Three bounds limit guessing.

    • The per-CPF lockout. The Server locks a CPF out of session creation after Sessions:MaxPinFailures (5) failures inside Sessions:PinFailureWindow (15 min), counted across every API key and answered 429 TOO_MANY_PIN_ATTEMPTS. A request books at most one failure. A successful creation does not clear them: they age out with the window.
    • The Failed-Proof Budgets. Every PIN Proof a workstation gives for a CPF that does not check counts against that machine and that CPF, whoever else matched, under the same two figures. Once a machine has spent its budget it is not challenged or prompted for that CPF until its failures age out; the Server logs a Warning and writes pin.proof-budget-spent. The Certificate Vault has one budget per CPF on the same terms: every password it tries that opens no bag counts once per request. So a party holding an API key and an admitted machine can test only a handful of guesses per holder per window, through the machine or through the vault. A budget is charged to the machine that gave the proof and to no other, but a holder's own machine holding several of their certificates spends one failed proof per certificate for each wrong PIN, so it can be passed over before the lockout trips. A pin.proof-budget-spent row is therefore not proof of guessing: read it beside the CPF's pin.compared rows. Configuration has the three bounds side by side.
    • The workstation's own limit. It refuses entries for a certificate after Tray:MaxConsecutivePinFailures (3) token rejections (or wrong A1 PINs) in a row, for Tray:PinFailureCooldown (5 min).

    None of them can make hardware lockout impossible: a holder can still lock their own token by mistyping.

  • PINs and PIN Proofs never appear in the Audit Log, the Server log or the Workstation Service log. A proof is brute-forceable offline by whoever holds its nonce, so it is kept out of every log as the PIN is. The PIN Cache is memory-only by default; PinCache:SurviveRestart persists it under DPAPI machine scope in a SYSTEM-and-Administrators-only file, a documented deviation an administrator opts into.

The lockout and the budgets are keyed by the holder's CPF, whichever Company a session names: a holder's e-CPF and e-CNPJs usually share one PIN, so a guess at one is a guess at the other.

For a Vault Certificate, the pin a client sends is the password that opens the certificate's file. The Server uses it to open the key at session creation, holds it in memory for that one session, and never writes it down. Deactivating the session, or restarting the Server, drops it.

Per-Transaction PIN​

By default a Signing Session, once created, authorizes every signature under it on its own until it ends. An Administrator can switch a Client Application to Per-Transaction PIN, so that every signing call under the sessions it creates needs the holder's PIN for that call alone:

  • On a workstation, the holder authorizes each call at the Tray App, in the console session only, after seeing the Client Application's name and what the call signs (the document names, or the number of digests). The token checks the PIN, or the A1 PIN's verifier checks it for an A1. The Service holds the accepted PIN in memory, under Data Protection, for that call's signatures only, and wipes it when the call ends, its uses are spent, its time is up, the token refuses it, or the Server connection drops. It is never read from or written to the PIN Cache, so it cannot extend unattended signing for any other application's sessions. A sign that names no open authorization is refused. The PIN never crosses the workstation channel.
  • In the Certificate Vault, where nobody can be prompted, the client sends the password with each call. The Server checks it once per call under the same per-CPF lockout and the vault's Failed-Proof Budget as session creation, uses it for that call's signatures, and drops it; nothing is held between calls.
  • A machine holds at most Signing:MaxPendingAuthorizations (2) calls waiting for their holder, so a client holding one sessionId cannot queue prompts without bound or fill the global throttle with waiting calls.

It does not make a session safe to leak: a sessionId still opens a session for whoever holds it, but under this mode nothing is signed without the holder's PIN for that call.

Sessions without a PIN​

Since Server 1.18.0 an Administrator can also switch a Per-Transaction PIN application to create its sessions without a PIN, so that the holder types the PIN at their Tray App for each call and the integrator never handles it. The switch is offered only while Per-Transaction PIN is on, turning Per-Transaction PIN off clears it, and turning it on asks first, saying what it accepts:

  • A key of that application can create a session for any CPF that has a certificate on the platform, read that certificate, and replace that CPF's active session, without the PIN. The certificate (name, e-mail address, chain) is public material, but replacing a session can end another integrator's session for that CPF, repeatedly. The key cannot sign: every signing call under such a session still needs the holder, at the Tray App or as the call's pin.
  • Nothing is proven at creation. The session binds only where exactly one certificate could serve it, with no PIN Proof, no prompt and no vault opening, and nothing is counted against the per-CPF lockout or any Failed-Proof Budget. A sent pin is proven exactly as before; the lockout still refuses a locked-out CPF with or without one.
  • A certificate row an admitted machine announced without holding the certificate can bind where it is the only candidate, for example while the holder's own machine is offline. That machine then receives the session's signing calls and learns what the application asks to have signed. It learns no PIN, because none is sent, and it cannot answer with a signature the certificate verifies: the signature service checks a document's signature against the certificate, and a Hash Signing answer fails whatever verifies it.

The risk is bounded to applications an Administrator switched on. The Audit Log tells the two kinds of creation apart: session.created carries pin, "proven" or "none", and names the machine the session bound to, and the switch writes admin.application-session-pin-waived and admin.application-session-pin-required.

Hash Signing​

Hash Signing (POST /v1/signatures/hashes) hands the holder's key digests the Client Application chose itself. A digest carries nothing that says what it is a digest of, so nothing on the platform can inspect what is being signed: an ICP-Brasil e-CPF is also its holder's TLS login to services such as gov.br, and a relayed TLS handshake hash is exactly such a digest. That is why it is off for every Client Application until an Administrator grants it, per application and never per key, and why the grant is checked before the session is even looked up. Grant it only to an integrator you trust to build its own signed structures, and read its hash-signing.* rows in the Audit Log, one per digest.

The A1 PIN​

An A1 Certificate in a workstation's machine store has no token and no PIN of its own. It signs through TrustBridge only once its holder sets an A1 PIN for it at the Tray App, in the console session, on that machine. The Workstation Service keeps a salted PBKDF2 verifier, never the PIN, in a file under C:\ProgramData\TrustBridge\ protected like the PIN Cache's. Changing it asks for the current one, and a forgotten one is recovered by removing the certificate from the store and installing it again. The verifier is what the machine's PIN Proof is keyed by, so checking it costs the Server one key derivation per machine per request. It is deleted when the certificate leaves the machine and by a true uninstall, and an upgrade keeps it.

Whoever can manage a machine's certificate store, or sits at its console before an A1 PIN is set, controls who may sign through that A1. An A1 PIN decides who may sign through TrustBridge; it does not protect the key, which stays as usable in the store as it was.

The Key Ring Protector​

Both of the Server's key rings are rows in its database:

Key ringProtectsIf it cannot be read
web-sessionsAdmin cookies, antiforgery tokens, Blazor circuitsEvery administrator signs in again, and nothing else
certificate-vaultThe private key of every Vault CertificateThe host stops. Minting a fresh key would silently orphan every certificate already stored

Both are sealed under one X.509 certificate with an RSA key that the platform does not keep: the Key Ring Protector, supplied as KeyRingProtector:Certificate (base64 PKCS#12). Every Server needs one, vault or no vault, because the web-session ring needs it too. Whoever can read the protector and the database can read every Vault Certificate, so the protector is a secret in its own right, and the .env or app setting holding it deserves the same care as a private key.

Back up the PKCS#12 permanently, somewhere the platform cannot reach. Losing it makes every Vault Certificate unopenable by anyone, this platform included. There is deliberately no escrow. A Key Vault reference on Azure is a convenient home for the running Server and is not a backup.

Rotation is additive. A new protector seals new keys; it never re-wraps what is already stored. The outgoing protector moves to KeyRingProtector:RetiredCertificates:{n}:Certificate and its PKCS#12 is kept forever. Delete a retired protector and you have destroyed everything sealed under it, with every symptom pointing at the database. The Server states the posture at every start: the current thumbprint and the retired count.

The Certificate Vault​

Turning the tier on brings three things:

  • The Lacuna PKI SDK licence takes on a second job. Every deployment already has one, because it is the licence the whole fleet runs on (below). With the vault on, the Server also validates what it holds through the SDK, which reads nothing unlicensed. With the licence refused or lapsed, every Vault Certificate reports UNTRUSTED and every import is refused, said once at start as a Critical line and in a banner on every admin page.
  • A PKCS#12 must be in the legacy PFX shape. The library the platform signs with cannot read a key shrouded under pkcs5PBES2, which is what openssl pkcs12 -export writes unless told -legacy, and what recent Windows export paths write too. Such a file is refused at import, naming the re-export:
    openssl pkcs12 -export -legacy -in holder.pem -inkey holder.key -out holder.pfx
  • The vault can latch shut when the Server starts before its database answers. See Operations.

The import password is used to prove the bag opens and is never stored: nothing can open a stored bag unattended, which is also why no unattended pass can check old bags for the legacy shape. Wrong passwords sent at session creation spend the vault's Failed-Proof Budget for that CPF (PIN handling). There is no export or download path. A certificate leaves the vault by being deleted, which destroys the key and cannot be undone, and the confirmation asks for the CPF to be typed. Out of use stops a certificate opening sessions and signing while keeping it.

A vault-bound Signing Session does not survive a Server restart, because the secret that opens the certificate is held only in memory. That is a property of the custody, not a fault.

API keys​

An API key authenticates a Client Application to the whole contract surface. It is issued on the Client Applications screen with a name and an optional expiry, shown once, and stored only as its SHA-256. Every accepted key of an application is equivalent; the key scopes nothing.

  • Revoke a key and the next request bearing it is refused, permanently.
  • Disable an application and every one of its keys is refused at once; enabling restores them.
  • Expire a key at the end of a UTC day, or clear the date to make it open-ended again.
  • Rotate by issuing a new key beside the old one, deploying it, then revoking the old one.

Missing, unknown, revoked, expired and disabled-application keys are all refused identically with 401 UNAUTHORIZED, so a caller learns nothing about which it was. Each key has its own trail in the Audit Log, refused calls included.

A sessionId and a signatureId are bearer tokens for what they name: any accepted key can use a session or fetch a document whose id it holds. Do not hand either to a party who should not have it.

The Test Signature​

A Test Signature asks for an API key, a CPF and its PIN (which may be left empty where the key's application creates its sessions without a PIN), and makes a real signature. The Server holds no key of its own for it: the run is made as the Client Application whose key was typed, so a demonstration begins by handing somebody a key, and whoever holds it could use it against the contract directly. Issue a dedicated key for demonstrations and revoke it afterwards.

From Server 1.23.0 the page writes every field back into the page that answers the form, the key and the PIN included, so that Sign again is one press. That answered page is sent no-store, so it is not kept in the browser's cache, but the key and the PIN are in it for as long as the tab is open: a demonstration on a shared machine ends by closing the tab. The Server still logs neither, and forgets both once that page is written. (Before 1.23.0 the page never wrote either back.)

The admin console's page is behind the admin login and its second factor, for Administrators only. The public page exists only where TestSignature:Enabled is on, which puts a signing form at the root of the host; like the other exposure switches it is deployment configuration and never a screen. Any CPF may be tried there, a run replaces a session already active for that CPF and CNPJ, and a wrong PIN counts towards the holder's lockout, exactly as a session created with that key from any client would.

Leave Diagnostics:ServerTiming off in production. With it on, every caller holding an API key is told how long the signature service and the deployment's tokens take, and which sessions sign on a workstation rather than in the vault, which the contract otherwise keeps from them.

Admin accounts and the second factor​

Admin Accounts are local. Every account carries a Second Factor (TOTP from an authenticator app) in both roles, enrolled by its owner and known to nobody else. Recovery Codes are single-use substitutes, issued as a set at enrollment and shown once. An Administrator can clear somebody's second factor, which invalidates their Recovery Codes and forces re-enrollment, but can never read or set it.

What the database holds. Passwords, API keys and Recovery Codes are stored only as hashes; a Recovery Code is hashed the way a password is (salted PBKDF2), and codes written by a Server before 1.10.0 are hashed at the first start after the upgrade, without signing anyone out. The authenticator secret is stored in clear text, because the Server has to compute codes from it every thirty seconds. Someone holding a copy of the database, such as a stolen backup, can therefore compute any administrator's authenticator codes; the password still stands, but for them the account has one factor rather than two. Protect database backups accordingly.

Admin:SecondFactor:Required (default true) is deployment configuration and is deliberately absent from the Settings and users screen: a switch that disables the second factor, reachable from inside the console it protects, is a switch an intruder flips on the way past. A row saved in the database under that key is ignored. The same guard applies to the Admin:CertificateSignIn:* keys and to Vault:Enabled.

Passwords are at least twelve characters. There is no reset mail; an Administrator sets a new password on the Settings and users screen and hands it over in person, and the account's existing sessions end within five minutes. Disabling an account keeps it, because the Audit Log attributes its past actions to it.

Certificate Sign-In​

Where a deployment turns on Admin:CertificateSignIn:Enabled, an account can also be opened by an ICP-Brasil e-CPF read through Lacuna Web PKI, with no password and no Second Factor step (Admin console). What that rests on:

  • Possession is proved by signing a challenge the Server minted: single-use, short-lived, bound to the hostname and to the browser's pre-authentication cookie, and signed as raw bytes rather than as a digest the Server chose. It stops replay, not real-time relay by a page on another domain, which is the same limit a relayed Second Factor code has.
  • The certificate is validated like every other certificate on the platform, against the Server's ICP-Brasil roots and Trusted CAs, with revocation checked at every sign-in. Only ACTIVE passes. An e-CNPJ never signs anybody in.
  • The binding is a CPF, never a thumbprint or an issuer. Any certificate the Server trusts that claims an account's Bound CPF opens that account, so a renewal keeps working, and so does a certificate from any authority the Server trusts. Two consequences follow, and both are accepted:
    • An Administrator sets every Bound CPF, and never their own. Nobody proves a CPF is theirs; it is typed on the accounts screen, so a binding is exactly as trustworthy as the Administrator who typed it. An Administrator could type their own CPF on a colleague's account and sign in as them; the admin.account-bound-cpf-set row names who did it, and the colleague's own account page shows who set their binding and when.
    • A Trusted CA is a credential issuer for the console. Whoever controls an authority added on the Trusted CAs screen can issue a certificate carrying any Bound CPF and sign in as that account. The admin.trusted-ca-added row names who added it, and each admin.certificate-sign-in row records the presented certificate's thumbprint and issuer. Recovering from a compromised Administrator session includes reviewing the Trusted CAs it added. On a deployment with Certificates:Validation:UseLacunaTestPki on, an account bound to a sample certificate's CPF is open to anyone who downloaded that sample.
  • It is rate-limited rather than locked out. The challenge is limited to ten a minute per address (an IPv6 address counts by its /64), and at most eight certificate checks run at once. A failed certificate sign-in never touches the password lockout, and an account locked out by wrong passwords can still sign in with its certificate. Behind a reverse proxy, the per-address limit counts browsers only where the proxy is named in ForwardedHeaders:TrustedProxies (Network posture).
  • Replacing or clearing a Bound CPF ends that person's sessions within five minutes, as setting a password does. To take an account back from someone, clear its Bound CPF as well, or disable the account.
  • The login page remembers the way in (Server 1.20.0). An accepted password or certificate, even one a Second Factor step then follows, writes a cookie, TrustBridge.SignInCredential, scoped to /admin/login and kept for a year, that names which of the two it was, so the login page opens on the same one next time. It is not a credential, authorizes nothing and outlives a sign-out. It is written and read only while Certificate Sign-In is on, and the other way in stays one link away.

Passwords can be switched off with Admin:CertificateSignIn:Required=true. Before doing so, make sure every person who needs the console holds a Bound CPF, that at least two Administrators exist (a lone one has nobody to set theirs), and that a reverse proxy, if there is one, is named. Turning it on does not end sessions already opened with a password. At every start while it is on, the Server logs a warning counting the enabled accounts with no Bound CPF, how many of those are Administrators, and how many enabled Administrators there are: when the second number reaches the third, nobody can administer the platform. The Server refuses to start with it on while Admin:CertificateSignIn:Enabled is off.

Break-glass: a lost authenticator​

An Administrator who has lost their device can be locked out even while the platform believes another Administrator can sign in. The way back is from the deployment, not the screens:

  1. Set Admin:SecondFactor:Required=false and restart the Server.
  2. Let them sign in with their password and enroll a new authenticator.
  3. Set it back to true and restart.

The Server announces the weakened posture at every start while it is off, not just the first, because a break-glass nobody closed again is the failure worth warning about.

Break-glass: no certificate signs in​

With passwords switched off, several things end every Certificate Sign-In at once: removing the Trusted CA the administrators' certificates chain through, a lapsed PKI licence, a Web PKI licence that no longer names the hostname people type, a browser update that breaks the extension, or the last Administrator's Bound CPF cleared. The Server never refuses to start over this, so:

  1. Set Admin:CertificateSignIn:Required=false and restart. Password sign-in is back, with every account's password and Second Factor where they were.
  2. Sign in with a password and fix whatever stopped the certificates.
  3. Set it back to true and restart. Nothing reminds you to.

Workstation admission​

A machine has no shared secret to join with, and looking for one is looking for something the product deliberately lacks. It mints a Workstation Key in its TPM where one is usable (otherwise in software), announces itself, and waits for an Administrator to admit it. Once admitted, it proves itself on every connection with a short-lived signed Workstation Assertion rather than a credential: nothing is issued, nothing is stored, and a captured assertion has already been spent.

The Installer Key that also signs an admission request is anti-abuse, not authentication: every copy of TrustBridge holds the product's private half. A deployment that wants a key nobody else holds mints one, names its public half under Admission:InstallerKeys (which replaces the product's key), and delivers the private half through its own installer.

What admission is worth is bounded and said on the screen: with no out-of-band channel to check a fingerprint against, a machine is noticed rather than verified. The controls around it: Admission:MaxPending bounds the queue, Admission:RequestsPerAddress bounds a source address (an IPv6 address counted by its /64), Admission:PendingExpiry lapses abandoned requests, and Admission:RequireHardwareBackedKeys refuses software-keyed machines. The key posture is the machine's own claim; TPM attestation is out of scope.

An admitted machine also chooses which certificate rows it announces, and that is why it never receives a PIN: an announcement can carry a holder's real, public certificate, so the Server only ever asks a machine to prove it holds the PIN, and budgets the proofs that fail (PIN handling).

Revocation on the Workstations screen refuses a machine for as long as its row stands and does not need its cooperation. A machine whose disk was cloned with a software key is a cloned identity, which is what the hardware-only posture exists to refuse.

Remove (Server 1.21.0) is housekeeping beside Revoke, not a security control. It forgets a machine that was ever admitted: its row, its Certificate Registry entries, its Workstation Report, its failed PIN Proofs, and with the row the block on its key, so the same key asking again arrives as a new Pending machine that an Administrator has to admit. Removing a Revoked machine therefore lifts its refusal, and the confirmation says so. A lost, stolen or compromised machine is revoked; a deployment that wants a permanent block does not remove it afterwards. A connected machine is disconnected on removal, and a session still bound to it signs nothing more. The Audit Log keeps every row about the machine and gains an admin.workstation-removed row naming it in full, and the Signing Sessions and Signing Operations made through it remain.

Trust anchors​

The platform trusts the ICP-Brasil roots the PKI SDK carries by default. A private certification authority is added as a Trusted CA on the admin console, in force on the running Server and pushed to every admitted machine. Anyone who can add a Trusted CA can make certificates of their own validate as ACTIVE, and, where Certificate Sign-In is on, sign into the console as any account whose Bound CPF they put in one. That is why the action is an Administrator's and is audited, and why an authority discovered over the web from a URL the untrusted certificate itself named is flagged for fingerprint checking before it is confirmed.

On a workstation, anchor files under C:\ProgramData\TrustBridge\anchors\ are the equivalent, behind an ACL the installer owns, and are disregarded while the machine holds a non-empty set from the Server.

Two switches widen trust and are worth knowing about: UseMachineRootStore (every public CA in the Windows root program, on a workstation) and UseLacunaTestPki (development only: Lacuna publishes the private keys of those sample certificates, and a Server with it on shows a warning banner on every admin page).

Certificates:Validation:RevocationGraceWindow (24 h) is the other trust decision: exactly how long a certificate keeps signing after its CA stopped answering, revoked or not. Zero is the strictest posture.

Documents signed with REST PKI Core​

Since Server 1.18.0 an Administrator can switch a Client Application to have its documents signed with REST PKI Core instead of Lacuna Signer. REST PKI Core builds the signed PDF around the one signature the holder's key makes; it never holds the key, and custody, the PIN, the PIN Cache and Signing Authorizations work exactly as before. What changes:

  • REST PKI Core keeps the signed documents. TrustBridge still keeps none. How long REST PKI Core keeps one is REST PKI Core's setting, and TrustBridge has no call that deletes one there. Depending on its subscription's settings, REST PKI Core may also mark the pages. The switch on the Client Applications screen says both.
  • Its trust is the security context's, not the Trusted CAs'. REST PKI Core validates the holder's certificate under the security context the deployment configured (RestPkiCore:SecurityContextId), and the Trusted CAs on the admin console are not applied to it. A deployment whose holders come from certification authorities beyond ICP-Brasil needs a custom security context, kept in step with its Trusted CAs by hand. The platform's own verdict still gates every signature; REST PKI Core's is a second check, and the two can disagree, in which case the client is answered CERT_NOT_ACTIVE. REST PKI Core checks revocation live, so the platform's RevocationGraceWindow does not carry over unless the security context is set to ignore an unknown revocation status.
  • RestPkiCore:ApiKey is a secret, for a REST PKI Core application with the Worker role, and it travels in every request, which is why the Server refuses to start with a RestPkiCore:Endpoint that is not https.
  • The Server reaches two of REST PKI Core's addresses: its endpoint, and its site address, where a signed document is downloaded from through a URL that carries its own access ticket, expires after about an hour and is never stored.
  • A refusal after the holder's key signed promises nothing either way: REST PKI Core may hold a signed document no Signing Operation names. Where a completion was left unanswered, the Server log says so in one Error line.

The PKI licence​

Certificates:Validation:License is required on every Server, vault or not: a Server without one refuses to start. It is also the licence the whole fleet runs on. The Server hands it to each admitted machine in the handshake, and only there; a pending machine never receives it. The machine holds it in memory only and is handed it again on every connection, so renewing it means replacing the value on the Server and restarting it, and the fleet picks it up as it reconnects. Nothing about the licence is written to a workstation's configuration or registry: an XML licence is written to a temporary file for as long as one load takes and deleted at once, because the SDK reads XML only from a file. The value is redacted wherever the handshake is logged.

A licence the SDK refuses leaves every certificate UNTRUSTED, on the Server and on every machine it is handed to, so nothing can sign and no Certificate Sign-In succeeds. A licence refused at start puts a banner on every admin page.

Server-delivered updates​

An Administrator can upload a Workstation release on the Workstation Releases screen and offer it to machines, which install it with nobody at them. The channel is built so that the Server cannot make a machine run anything but a genuine, newer TrustBridge Workstation package:

  • The Server refuses an upload that is unsigned, signed by a certificate not on the Publisher Pin compiled into the Server, or speaks a protocol outside the Server's window. Every Workstation release from 1.2.2 on is Authenticode-signed by Lacuna Software, RFC 3161 timestamped.

  • The machine checks the package itself, in this order, and refuses it at the first failure, whatever the Server said about it:

    1. the SHA-256 and length the Server announced;
    2. the Authenticode signature;
    3. an RFC 3161 timestamp;
    4. a signer on the Publisher Pin compiled into the Service already installed, not the one arriving;
    5. revocation, where only a pass, or a question that could not be answered, gets through;
    6. the TrustBridge Workstation's own UpgradeCode;
    7. a ProductVersion equal to the one offered and above the installed one.

    So the worst a compromised Server can do through this channel is install a genuine, newer release early: never another file, another product or an older version.

  • The download is authenticated by the machine's key. Each request to GET /workstations/releases/{version} carries a fresh Workstation Assertion, and a machine is served only the release offered to it. The package lands in C:\ProgramData\TrustBridge\updates\, which only SYSTEM and Administrators can write.

  • An operator can switch it off per machine with the Take updates from the TrustBridge Server Group Policy; the Workstations screen then reads Updates disabled by policy.

The Workstation Report​

From Workstation 1.8.0 a machine tells its Server about its own condition, unasked, after every handshake and whenever that condition changes, so that support can see a machine's trouble without being at it. The Workstation Report carries:

  • the PKI Licence as the machine holds it, its .NET runtime version and its switches;
  • the middleware it looks through, with each module's path, its file version and whether it loaded;
  • each token it sees, by vendor, model and serial, with the CPFs of the certificates on it and how close its PIN is to locking;
  • the smart-card readers Windows left failed, with their device instance ids;
  • the errors the Workstation Service and the Tray App logged at Error or Critical, grouped by message template, with the template's values, the logger category and the exception's type and message;
  • from Workstation 1.10.0, its console: the Console Account signed in there, by its Windows SID and its DOMAIN\user name where that resolves, whether that session is locked, whether a Tray App runs there and its version, and how many other sessions have one, without their accounts. The service reads the account itself, so it is reported while no Tray App runs, and sends the report again on a sign-in, a sign-out, a lock or unlock, a switch of user and a Tray App attaching or detaching.

PINs in any form, document bytes and signature bytes are never sent. CPFs, token serials, the Console Account and the values of a logged line may be. The Server keeps the last report of each machine, shows it to anyone who can read the Workstations screen, and deletes an error group that no report has added to for WorkstationReports:ErrorRetentionDays (30 days by default); nothing in a report writes an Audit Log row. A machine can be told not to report, with WorkstationReport:Enabled, the MSI's WORKSTATION_REPORT or the Report this machine's condition to the TrustBridge Server Group Policy: it then says only that it does not report, and the Server forgets what it held for it. The same switch keeps the console on the machine: with it off, no account or other part of the console leaves it. Removing a machine deletes its report.

Secrets and the logs​

The Server and the Workstation Service keep secrets out of what they print. On the Server, passwords, Second Factor codes, the authenticator secret, a new API key's value and hash, and the PKI licence handed to a machine are redacted wherever a command or a result would be logged. On a workstation, the PIN and any PIN Proof are never printed. The Tray App writes no log file at all, because a prompt's line names a CPF. A CPF does appear in Server log lines and Audit Log rows about a session or a budget, as it always has. From Workstation 1.8.0 the Error and Critical lines of the Workstation Service and the Tray App, with their values, also reach the Server in the Workstation Report, unless the machine is told not to report.

Network posture​

  • Workstations connect outbound only, over HTTPS to the Server. No inbound port is opened on a machine, and the Service–Tray channel is a local named pipe whose ACL is its whole access control.
  • The Server terminates its own TLS on Docker and must present a chain every workstation trusts; a reverse proxy can terminate it instead. On App Service the platform terminates it.
  • The caller's address comes only from proxies you name. On Docker behind your own proxy, list it in ForwardedHeaders:TrustedProxies (addresses or CIDRs; in the environment, ForwardedHeaders__TrustedProxies__0, …__1). X-Forwarded-For and X-Forwarded-Proto are honoured only from a listed peer; Host and the path are never rewritten. On App Service, ASPNETCORE_FORWARDEDHEADERS_ENABLED=true does the same job; set outside App Service it trusts whoever connects and the Server warns at start, and a Server with both set refuses to start. What a proxy left unnamed costs: the Audit Log's ClientIp, admission's per-address bound and the Certificate Sign-In rate limit all see the proxy's one address, so anybody who can reach the login page can spend everybody's sign-in attempts.
  • Eight realms share one port, told apart by path: the contract under /v1, the workstation channel, workstation admission, the Workstation Release download, the published documentation, the Landing Page, the health probe and the admin pages. A path outside every realm is answered with an API-key 401 before a browser sees it. /healthz, / and the documentation answer without a credential and deliberately reveal nothing about platform state. The admin cookie is honoured on the admin paths and nowhere else.
  • The global throttle (Throttle:ConcurrencyLimit, 200) is a circuit breaker over the contract surface against a runaway client, and cannot be switched off.
  • Never run a deployment as ASPNETCORE_ENVIRONMENT=Development: the image carries a development settings file with a sample licence and a shared test Signer's credentials, inert only because a deployment is Production.

What is written where​

RecordHoldsNever holds
Audit Log (database)Actor, subject ids, outcome, timestamps for every API call, session and signing event (each Hash Signing digest and Signing Authorization included), workstation, PIN and update event, Test Signature, admin action and sign-in; from Server 1.24.0, the Error Code an API call was answered withPINs, PIN Proofs, API keys, document content, signature bytes, an error's message and details
Server log (console, rolling file, Application Insights)What the Server was doing, with a correlation id; audit rows the database refused, at CriticalPINs, PIN Proofs, passwords, codes, key material, the PKI licence
Workstation Service log (Application event log, Tray diagnostics pane)Where each install-time value came from, connection state, refusals, verdicts, update outcomesPINs, PIN Proofs
Database (SQL Server, or PostgreSQL on Docker)Sessions, operations, registry, vault ciphertext, both key rings, admin accounts (password and Recovery Code hashes, authenticator secrets in clear, Bound CPFs), failed PIN Proofs, settings overrides, Workstation Releases (the whole packages) and their offers, each machine's last Workstation Report (token serials and CPFs, middleware, failed readers, error groups, the Console Account), Signing Activity counts (which name no holder)The Key Ring Protector, any signed document (the Signer's or REST PKI Core's), any PIN
/tmp/trustbridge-releases (Server)A copy of each stored Workstation Release to serve downloads from, and an upload while it is readAnything not already in the database
C:\ProgramData\TrustBridge\ (workstation; Administrators and SYSTEM only)The PIN Cache file when PinCache:SurviveRestart is on, the cached Trusted CA set, the A1 PIN verifiers, the anchor files; under updates\, a release being installed and the five newest install logsThe PKI licence, any A1 PIN itself

Nothing on either component writes a document to disk. Documents are held in transit only, in memory, bounded by Signing:MaxDocumentBytes times Signing:MaxBatchItems. A signed document is kept by the signature service that built it: Lacuna Signer, or REST PKI Core for a Client Application switched to it.

View as Markdown