Skip to content

Security: cachix/factorseal

Security

SECURITY.md

Security

Factorseal is an unaudited prototype. Do not use it for production secrets until its native Linux, macOS, and Windows acceptance suites pass and an independent review has been completed. The remaining checks are tracked in the security release gates.

Please report suspected vulnerabilities privately to the maintainers. Do not open a public issue before a coordinated fix is available.

Vault security boundary

The per-user vault is the intended product architecture. Its keyring interface is one authorized way to retrieve and update credentials:

  • one per-user process is the sole owner of the embedded Turso database, the lease-scoped installation root/index capability, and active operation keys;
  • every document generation has an independent random DEK; its current-record snapshot and separate value-free history are encrypted with AES-256-GCM and authenticated by one commit signed by the installation's device key;
  • secret names and values exist inside encrypted documents, not plaintext SQL columns or filenames;
  • the vault root directory is created mode 0700 on Unix and with a protected, owner-only DACL on Windows, and both are re-validated every time the vault is opened;
  • project and address enumeration requires a separate authenticated List grant, is available only while unsealed, and never returns secret values;
  • a bounded local protocol authorizes transport-derived user, executable, and application identities against durable scoped grants;
  • duplicated and oversized requests fail closed, and secret buffers use zeroizing storage where the API permits;
  • idle and absolute unseal leases, explicit sealing, termination signals, startup cleanup, native suspend/shutdown/session notifications, and live expiration all converge on the same store shutdown path.

Desktop launches the CLI's dedicated desktop-worker as the key owner. The worker hardens its process before reading a bounded bootstrap over inherited pipes. The password is wiped after create/unseal, before database ownership or service startup. Desktop uses authenticated native IPC for subsequent requests; it does not retain the installation root or an unlock password during the lease. Closing its lifeline (including Desktop exit/crash) seals and terminates the worker, with a four-second emergency exit if teardown blocks. Native lifecycle and lease monitors also run in that worker. The CLI must be installed alongside Desktop or selected through FACTORSEAL_CLI_EXECUTABLE.

On Linux, Secret Service item labels and lookup attributes stay in the encrypted vault index. Desktop drops that index on seal and does not persist a plaintext search cache. Credential lookups while sealed return IsLocked immediately; users must manually unlock Desktop before applications can look up credentials. Clients that explicitly request Unlock can still use the normal prompt flow.

Desktop secret-entry fields use bounded locked, guarded buffers, omit undo/copy history, and send only masked text to the renderer. Submitting, leaving a secret view, or sealing clears the inputs. Clipboard, native input methods, crypto libraries, and operating-system internals can still hold copies; this is not a claim of comprehensive memory erasure.

Every unlock group is hardware-bound. Factors inside a group are AND requirements and independently wrapped groups are OR alternatives. Password groups use memory-hard Argon2id by default. The opt-in FIPS profile instead uses PBKDF2-HMAC-SHA-256 with 600,000 iterations. Both encrypt the installation root with AES-256-GCM before one hardware key per group wraps it. The root derives the document-index key and authenticates the wrapped signing capability and each generation's independently wrapped DEK. Biometric groups gate their hardware keys with the platform biometric policy; biometric-only groups do not contain a password layer. Password files are accepted only as private bounded regular files and are intended for short-lived session launch handoff. The same open handle is checked and read. Unix requires current-user ownership, no group/other access, and no final symlink; Windows rejects final reparse points and requires a current-user-owned file without ACL grants to another account. Export files are created privately before any bytes are written, then atomically replace the destination: mode 0600 on Unix and a protected current-user-only DACL on Windows, even in a shared destination directory.

All public vault-creation and archive-encryption entry points require UTF-8 passwords of at most 64 KiB with a zxcvbn score of at least three. The desktop and CLI share that policy. Existing vault unlock and archive decrypt deliberately continue accepting their original factors, including weaker legacy passwords. Software keyring and DPAPI-only fallbacks are rejected.

Each biometric HardwareSeal unseal performs a native authorization ceremony. Factorseal then holds only the installation root and document-index key for its independently bounded idle and absolute lease. Document DEKs and signing capabilities are root-unwrapped into zeroizing memory only for an operation. For new macOS 26+ vaults, this capability is an opaque Secure Enclave ML-DSA-65 reference; the private signing key stays in the enclave. Other platforms and existing vaults retain software signing seeds. Native cancellation, denial, unavailable UI, locked session, and invalidated credentials remain distinct vault errors; unavailable hardware and unsupported policy are distinct as well. None is treated as a prompt success or silently downgraded.

Cryptographic profile and FIPS status

Every persisted vault profile uses AES-256-GCM for authenticated encryption, SHA-256 and HMAC-SHA-256 for digests and keyed identifiers, and FIPS 204 ML-DSA-65 for device signatures. The default profile uses Argon2id for password-containing unlock groups because it is memory-hard. The opt-in FIPS profile uses PBKDF2-HMAC-SHA-256 instead. Its NIST-standardized symmetric, password-derivation, and post-quantum algorithms are intended to make a future validated provider and deployment boundary possible.

The current RustCrypto implementations have not been validated through CAVP or CMVP, Factorseal has no FIPS 140-3 certificate, and algorithm selection alone does not make a product FIPS compliant or validated. Argon2id is not a FIPS-approved KDF and is therefore excluded from the FIPS profile. Deployment status also depends on the exact TPM, operating-system module, device configuration, build, entropy source, approved operating mode, and product boundary. Platform biometric ceremonies may depend on classical algorithms and are not claimed to be completely post-quantum certified.

Authenticated transports

  • Linux uses a mode-0600 Unix socket, SO_PEERCRED, same-UID enforcement, peer PID, and a digest of /proc/<pid>/exe. A peer that is being traced, or that was started with LD_PRELOAD or LD_AUDIT in its environment, is rejected before its executable is resolved.
  • macOS uses a mode-0600 Unix socket, kernel peer credentials, peer PID, and an audit token, then binds the grant to the executable digest.
  • Windows uses a local-only named pipe with a current-user/System/admin DACL, impersonates each client, verifies its immutable SID against the vault SID, and binds the grant to its PID-resolved executable digest.

Clients authenticate the connected server before sending any request bytes: Unix clients require their own UID; Windows clients require their own SID as both pipe-object owner and server-process identity. Windows opens use identification-only security quality of service, not full impersonation.

Caller identity is never accepted from request JSON. Replacing or updating an executable changes its digest and invalidates its grant. The digest is taken from a descriptor opened once, so the path, size, and bytes always describe the same image, and the peer's process start time is compared before and after resolution, so a reused process ID cannot inherit another process's grant.

Pending project approvals are capped at 128 globally and 16 per authenticated caller. A monotonic sliding minute permits at most 128 new approvals globally and eight per caller, including approvals subsequently granted or denied. Overload rejects new approvals without evicting existing ones. Repeating a pending request returns its original identifier and expiry without refreshing the approval UI. These are lease-local abuse controls, not isolation from a same-user process that can run different executables or restart the vault.

Metadata and lock files reject final symlinks/reparse points and non-regular files through the opened handle; Unix FIFO opens are nonblocking. Password, metadata and lock inputs also reject hard links. Ancestor path replacement and database sidecar handling still require separate review. Windows root validation checks current-user ownership as well as its protected DACL; child-file ACL inheritance also has a native regression test, but two-account acceptance remains required.

Security events use fixed, saturating counters for authorization denial, approval creation/decisions/limits, duplicate requests, malformed requests, integrity failures, seal requests and failed memory protection. They contain no names, fields, values, caller IDs or timestamps. Recording performs no allocation or disk I/O; existing diagnostics snapshots include the aggregate counters. These process-local events are operational evidence, not a durable or tamper-proof audit log.

Every externally controlled parser family has an opt-in fuzz harness. CI runs it on changes and daily, using synthetic corpora, address sanitization and resource limits. This complements the native and regression tests; it does not establish exhaustive malformed-input safety.

Broad administrative and compatibility authority

A permission manager can inspect and change grants and use portable vault export/import operations. Granting that capability therefore grants broad access to durable entries; it is not a metadata-only role. First-party CLI/Desktop executables receive this authority when initialized or reauthorized.

The Linux Secret Service collection is shared with applications on the user's session bus. It does not provide separate per-application vault grants for each keyring item. Its standard plain and legacy DH session algorithms are compatibility transports, not FIPS or post-quantum protocol claims. Only use this adapter where that session-wide trust is appropriate.

Dependency maintenance

Pull requests and a daily scheduled workflow run the RustSec checker. Reported vulnerabilities, yanked crates, new warnings, expired exceptions and obsolete exceptions fail CI. Six transitive unmaintained-crate advisories currently have exact package/version exceptions, with an owner, expiry and migration task in the dependency policy. These are accepted maintenance risks, not resolved advisories; no vulnerability can use that exception path. Desktop's direct notify dependency was upgraded, but compatible GUI dependency parents still retain the six flagged crates. GUI libraries are outside the separately built CLI key owner's dependency graph.

Honest limitations

  • Personal documents retain per-item Automerge histories inside their signed, encrypted snapshots. Unlike the value-free audit log, this includes old and deleted secret values, and newly enrolled readers currently receive that retained history. There is no password-history pruning or cryptographic erasure claim. The experimental personal-sync feature provides root-wrapped reader keys, authenticated encrypted change packets, durable publication and incoming merge/conflict handling through lease-bound host-management APIs. The optional personal-sync-network library adds a keyless iroh courier and controller-signed transport membership. Desktop now offers QR/ticket pairing and explicit code approval through inherited private worker pipes. Its courier continues while sealed if Desktop remains open; it starts after sync setup. Paired counts and reachability do not claim remote application. Mobile camera integration, peer application receipts, a conflict-resolution chooser, controller transfer and removal UI remain unimplemented. See the implemented boundary for bounds, experimental cryptography, retained metadata, and integration requirements.

  • An OR policy is bounded by its weakest unlock group. Biometric-only access has no independent recovery secret and can be lost after hardware reset, biometric enrollment changes, or platform-key invalidation.

  • Native lifecycle monitors are implemented on all targets, but packaged artifacts remain development-only until suspend, shutdown, logout, and session-lock behavior passes on native machines.

  • A signed local commit chain detects modified content, missing generations, divergent writers, and partial rollback when a newer protected head or commit remains, including a single document rewound while the global head is untouched. The chain is a tamper check, not an audit log: once it grows past a bound it is re-signed and compacted down to the current state of every document, and superseded generations are discarded. Every generation is encrypted under a fresh document key and each persisted snapshot contains only current records. Deletion is logical, not cryptographic erasure: retained wrapped keys and historical ciphertext in database remnants or backups can recover older values when the installation root is available. Checked WAL checkpoint/truncation reduces retention, but does not erase storage-device remnants, free pages, snapshots, backups or already exported copies. The value-free change history beside each document is as trustworthy as the installation root holder during a lease; it is a record for the user, not an audit log. It cannot detect rollback of the complete vault directory. Detecting that needs a checkpoint held outside the directory; the offline MVP does not claim whole-directory rollback detection. The offline integrity profile precisely defines this exclusion and the requirements for a future witness.

  • The implemented factorseal provider endpoint uses SecretSpec's typed IPC protocol over private standard-I/O pipes and translates requests into the disposable, project-partitioned secretspec-provider-cache document kind through the native VaultClient. The endpoint executable—not the SecretSpec CLI or embedding application—is the authenticated vault principal. Its IPC dependency is still pinned to an unpublished Git revision. For the default vault root, init publishes the user's provider claim and the agent refreshes it at startup; installed end-to-end conformance remains required on every target.

  • Linux executable authentication depends on access to the ptrace-gated /proc/<pid>/exe link. The current systemd user unit therefore cannot use filesystem mount-namespace hardening. A verified IPC sandbox/application identity or different broker design is required to close that isolation gap.

  • The Linux personal sync helper hard-requires fully enforced Landlock ABI 3 (kernel 6.2 or later) and refuses to start otherwise; there is no weaker fallback confinement. Kernels older than that, including Debian 12 and Ubuntu 22.04 stock kernels, cannot run personal sync. The parser helper relies on seccomp only and has no such requirement.

  • Executable identity is resolved after the connection is accepted, and no supported platform reports the image a peer had at connect time. A same-user process can therefore connect, queue its request, and only then execute a granted binary. Executable grants are defense in depth against the wrong program reaching the vault, not a boundary between same-user processes: a process that can execute a granted binary can also debug or preload it. The Linux transport rejects a peer that is under a tracer or carries a loader injection variable when it connects, which closes the direct debugger and LD_PRELOAD paths, but a same-user process can inject code and scrub those signals before it connects. The boundary the vault does enforce is the Unix user or Windows SID.

  • Both CLI and Desktop disable Unix core files before acquiring secrets. Linux key-owning agent, initialization, destruction, reauthorization and isolated approval helpers additionally disable process dumpability (including piped core collectors). IPC-only clients stay inspectable for executable authentication. Native emergency termination exits without deliberately creating a core dump. Keys and retained password/value/IPC buffers use dedicated locked allocations with inaccessible guards and full-region wiping on release. Linux excludes those pages from dumps and wipes them in fork children. Windows suppresses WER heap collection while retaining existing flags. See memory hardening for allocation failures, deployment limits, test coverage, and buffers outside this protection. Privileged inspection, comprehensive wiping of library/OS copies, Windows LocalDumps/external dump policy, native platform verification, code signing/notarization and independent audit remain limitations.

  • destroy removes local state, not every possible hardware authority. Linux and non-biometric Windows TPM envelopes have no per-label persistent key to revoke. Retained copies can remain usable on the original TPM with valid factors; one surviving OR unlock path suffices. No backup-revocation or cryptographic-erasure guarantee is made.

  • Apple storage uses device-only, non-synchronizing Data Protection Keychain items with the requested access control. Opening a protector also requires successful transient Secure Enclave key creation. The probe does not itself wrap the vault root; native signed/entitled package tests must verify the Keychain policy and rejection of machines without a Secure Enclave.

  • Deadlines are checked after queueing, before authorization and on completion; transport writes are bounded by the lease and known result/grant expiry. Bytes already released to an authorized client cannot be withdrawn. A native agent independently terminates if a wedged worker prevents timely teardown; library embedders do not opt into terminating their host process and must provide process isolation for a hard key-retention bound.

  • Windows biometric groups encrypt a TPM sealed-data object under a Windows Hello platform-credential PRF output. Native acceptance must establish PRF support, TPM binding, timeout/cancellation behavior, the application-owned prompt window, and the supported Windows Hello prompt before the release gate can pass.

  • Software ML-DSA-65 signing seeds are root-wrapped and exist in zeroizing vault memory only while signing. New macOS 26+ vaults instead use a non-exportable enclave key through a root-wrapped reference. Existing identities are not rotated on upgrade. The retained installation root still has authority to unwrap every local document during an active lease, so code execution in the unsealed process remains outside this protection.

  • Hardware binding cannot prevent an already authorized or compromised client from exfiltrating a secret returned to it.

  • Encrypted .factorseal archives provide portable backup and restore into a newly initialized hardware-bound vault. They require a separately retained archive passphrase and exclude device keys, application grants, history and disposable provider caches. Hardware loss still loses any data that was not exported beforehand; copying the native vault directory is not portable recovery. Archives are not automatically created or revoked by destroy.

  • The bounded request-ID window is an idempotency guard against a client resubmitting a request, not a replay defense: the local transport is a peer-credentialed, owner-only socket or pipe with no intermediary to replay through.

  • The embedded database is a pre-release Turso build and the sole durable store. Its crash consistency is trusted for the one-transaction commit path; the signed commit chain detects a torn or tampered result but cannot repair it.

There aren't any published security advisories