Status: Living reference document Audience: Contributors, security reviewers, architects Scope: Security guarantees, trust components, session and audit model
This document specifies the security model of NEXUS-Q: what it means for the system to be "secure", which components are trusted, how sessions and authorization work, and how the audit chain provides tamper-evidence.
Where THREAT_MODEL.md says "against whom we protect", this document
says "how the protection is organized".
NEXUS-Q is secure if, under the assumptions in
THREAT_MODEL.md §8 and the trust model in §3 below:
- Confidentiality holds: data encrypted under a key cannot be read by anyone who does not possess the key.
- Integrity holds: any modification of protected data is detected before the data is used.
- Authenticity holds: signatures verify only for the actual signer.
- Availability holds: operations fail closed, never open.
- Auditability holds: security-relevant events are recorded and cannot be tampered with undetectably.
Each of these is defined more precisely in the sections that follow.
NEXUS-Q does not attempt to be:
- A general-purpose OS security layer (no SELinux, no seccomp policy).
- A network security layer (no firewall, no TLS termination).
- A user authentication system (it consumes credentials, does not provide them).
- A code-signing or sandboxing layer for other applications.
The following components are trusted in the security model:
| Component | Why trusted |
|---|---|
| NEXUS-Q library code | Our code; reviewed, tested, fuzzed |
| Rust standard library | Widely reviewed; part of our TCB |
| Chosen crypto crates | Audited, minimal, pinned (see CRYPTOGRAPHY.md) |
| OS CSPRNG (when healthy) | Foundation of all randomness |
| Hardware backends | Trusted when attested; see §3.3 |
The following are not trusted:
| Component | Notes |
|---|---|
| Network | Assume hostile; all inputs validated |
| Filesystem | Assume attacker can read/write if disk is stolen |
| Other processes | Same machine, different user = hostile |
| The OS itself | Assume possible compromise; hardware mitigations apply |
| User input | Assume malformed or malicious |
| System clock | Assume can be manipulated by local attacker |
When a hardware backend is used:
- We trust it only if attestation succeeds (Fase 9).
- Attestation is only as good as the hardware vendor's implementation.
- We document per-vendor trust assumptions.
- We do not trust "trust us, we're secure" claims; attestation must be cryptographic and verifiable.
From ARCHITECTURE.md §4, the four layers are:
- L4 Consumers (untrusted — public API validates everything)
- L3 Core (trusted — our code)
- L2 Backends (semi-trusted — software is our code, hardware is attested)
- L1 Platform (untrusted — OS, drivers, devices)
The vault has a small state machine, and access is granted via sessions.
| State | Meaning |
|---|---|
| LOCKED | No KEK in memory. Only metadata is readable. |
| UNLOCKED | At least one KEK is in memory. Operations allowed per policy. |
| SEALED | Temporarily locked; no new operations, existing ones finish. |
| COMPROMISED | Detected tamper or policy violation; operations denied until admin intervention. |
Transitions:
LOCKED ──unlock──► UNLOCKED ──lock──► LOCKED
│
├──seal──► SEALED ──unseal──► UNLOCKED
│
└──tamper──► COMPROMISED (terminal until reset)
A session is the unit of access. Created when the vault is unlocked with a credential, ends when:
- The user explicitly locks the vault.
- The session times out (configurable, default 15 minutes of inactivity).
- The process exits.
- The vault enters COMPROMISED state.
Sessions carry:
- The identity that unlocked the vault.
- The set of KEKs currently loaded.
- The authorization scope (which keys, which operations).
Sessions do not carry:
- Raw password material is held in zeroizing buffers by the CLI and HTTP adapters and borrowed by the core only for KEK derivation.
- DEKs (loaded on demand, zeroized after use).
- Long-lived tokens.
On session end:
- All DEKs in memory are zeroized.
- All temporary buffers are zeroized.
- The KEKs are removed from memory (zeroized).
- The in-memory session object is dropped and no session token is retained by the core.
- Audit coverage records the operations that were actually performed; there is no separate SESSION_ENDED event.
Authentication is how a caller proves who they are. NEXUS-Q supports:
- Password-based: user provides a password; Argon2id derives a KEK and proves the user can decrypt the vault.
- Hardware-backed: user authenticates to a TPM/SE with a PIN or biometric; the hardware then allows access to the handle.
- Identity-based: a caller presents a signed challenge using an identity key (Fase 6).
Authentication does not grant access to specific operations. That is authorization.
Authorization is what an authenticated caller is allowed to do. It is implemented by the policy engine (Fase 11).
Policy decisions consider:
- The caller's identity.
- The requested operation (SIGN, DECRYPT, ...).
- The key's purpose and status.
- The current vault state.
- Context: time of day, process, hardware attestation, prior operations.
Default policy: deny. Access is granted only by explicit policy.
We keep them separate because:
- A user may be authenticated but not authorized for a specific key.
- Authorization can be changed without re-authenticating.
- Policies can be tested independently of authentication mechanisms.
Any error in any security-relevant operation results in denial:
- Parse error: reject input, do not partially process.
- Integrity failure: reject data, do not return partial plaintext.
- Policy denial: return error, do not fall back to a weaker operation.
- Hardware failure: deny operation, do not silently use software.
- Randomness failure: refuse to generate keys, do not use low entropy.
NEXUS-Q never returns "partial" results. If a decrypt operation fails, the caller gets an error and no plaintext. If a signature verification fails, the caller gets an error and no "maybe".
Error messages must not leak secrets or side information:
- "Decryption failed" — not "padding error" vs "MAC error" (avoids padding oracle attacks).
- "Authentication failed" — not "unknown user" vs "wrong password" (avoids username enumeration).
- "Operation denied" — not "policy X failed at step Y" (avoids policy fingerprinting).
Detailed errors are logged internally (in the audit log) but not returned to the caller.
The library must not panic on untrusted input. All parsing must
return Result. Panics are allowed only for:
- Programming errors (unreachable states, invariant violations).
- Failed allocations that indicate OOM.
Any panic reachable from untrusted input is a bug and must be fixed.
The audit log is not just a log; it is a tamper-evident chain.
Each audit event contains:
event_n = {
index: n (monotonic, starts at 1),
timestamp: UTC,
event_type: e.g., KEY_CREATED,
actor: identity or process,
subject: KeyId, VaultId, etc.,
outcome: success | failure | denied,
context: minimal, non-secret details,
prev_hash: H(event_{n-1}),
hash: HMAC-SHA256(audit_key, canonical_serialize(event_n without hash) || prev_hash)
}
Where H is SHA-256 (see CRYPTOGRAPHY.md §4.2), and
canonical_serialize produces a deterministic byte sequence (sorted keys,
explicit types, no whitespace).
To verify the chain:
- Read events in order.
- Recompute each HMAC with the vault-derived audit key and compare.
- Verify
prev_hashmatches the previous event's authenticated hash. - Verify
indexis strictly increasing. - Verify both the encrypted vault-side audit anchor and the authenticated audit-tail commitment are still consistent with the complete on-disk log.
Any mismatch = tampering or rollback detected. Verification is fail-closed and requires the vault-derived audit key.
The audit log is append-only at the application level:
- No API modifies or deletes individual events.
- Segment files are rewritten atomically as logical append-only records.
- The log is HMAC-authenticated with a vault-derived key.
- The encrypted vault body stores an audit-event anchor at the last successful vault persistence; unlock rejects a log that no longer contains that anchor.
- There is currently no retention/snapshot API.
- Private keys or any key material.
- Plaintext of user data.
- Passwords or password-derived material.
- Full ciphertexts (only hashes or KeyIds).
When auditing is enabled, the current implementation records key lifecycle events (key_created, key_activated, key_rotated, key_revoked, key_destroyed, key_access_denied), identity lifecycle/signature events, credential issue/verify/revoke events, envelope seal/open events, and policy changes.
The implementation does not emit every historical event name from older design documents. In particular, there is no generic key_used event and no independent session_ended or vault_locked event claim. This document describes only behavior that the code actually emits.
If the audit log cannot be written (disk full, permission denied), the operation is denied. We do not perform security-relevant operations without logging them.
A DEK is used for exactly one envelope and then zeroized. It is never reused across envelopes. This limits the impact of any single DEK compromise.
Each session has its own set of in-memory KEKs and DEKs. Closing a session zeroizes them. Sessions do not share secret material.
When the NEXUS-Q server runs as a separate process (Fase 14), it:
- Runs as a dedicated user, not root.
- Uses its own memory space (no shared memory with clients unless via authenticated IPC).
- Drops privileges after opening its storage.
- Is subject to the OS's process isolation (which we do not control but document).
Different uses of the same master key derive distinct subkeys via HKDF
with distinct info strings (see CRYPTOGRAPHY.md §5.4). This prevents
cross-protocol attacks.
NEXUS-Q is not formally verified, but it is designed with known models in mind.
In Bell–LaPadula terms:
- Subjects (callers) have a clearance (their identity's trust level).
- Objects (keys, data) have a classification (their sensitivity).
- "No read up": a caller cannot read data above their clearance.
- "No write down": a caller cannot write data below their clearance.
NEXUS-Q implements this via the policy engine: keys and data carry sensitivity levels, callers carry identities, and the policy enforces the lattice. We do not claim to be a full BLP system.
In Biba terms:
- "No read down": a caller cannot read data from a lower integrity level (prevents contamination).
- "No write up": a caller cannot write data to a higher integrity level (prevents corruption).
NEXUS-Q implements integrity via:
- Signed data (integrity attributed to the signer).
- Audit chain (integrity of the record).
- AEAD tags (integrity of ciphertexts).
Clark–Wilson defines integrity through well-formed transactions and separation of duties. NEXUS-Q approximates this through:
- Typed API (well-formed transactions).
- Policy engine (separation of duties between key owner and key user).
- Audit log (accountability).
We do not claim:
- That NEXUS-Q is verified against any formal model.
- That the implementation is free of bugs.
- That the policies expressible in NEXUS-Q cover every access-control need.
We claim that the design is informed by these models, and that deviations from them are documented.
This document implements defenses for the following threats from
THREAT_MODEL.md:
- T-01 (ciphertext tampering): fail-closed AEAD.
- T-04 (forged signatures): verification with strict algorithm binding.
- T-08 (key misuse): policy engine + purposes.
- T-09 (audit log tampering): vault-derived HMAC-SHA256 authentication.
- T-10 (silent downgrade): explicit algorithm binding, no negotiation.
Threats that the security model cannot mitigate in v1.0:
- O-01 (compromised OS): out of scope, mitigated by hardware.
- O-03 (cold boot): out of scope, mitigated by hardware.
- O-11 (DoS): partially mitigated; full DoS defense out of scope.
- Concrete timeout values for sessions (per platform)
- Retention policy for audit logs (how much to keep, for how long)
- Whether to support external audit log shipping
- Formal verification of the policy engine (future work)
- Snapshot format for audit chain rotation
Resolved in Fase 4 (Vault), Fase 11 (Policy), Fase 18 (Observability).
docs/THREAT_MODEL.md— adversaries and threatsdocs/ARCHITECTURE.md— module boundariesdocs/CRYPTOGRAPHY.md— hash, KDF, AEAD used heredocs/KEY_MANAGEMENT.md— key lifecycle events- Bell, D. E., LaPadula, L. J. — "Secure Computer Systems" (1973)
- Biba, K. J. — "Integrity Considerations for Secure Computer Systems" (1977)
- Clark, D. D., Wilson, D. R. — "A Comparison of Commercial and Military Computer Security Policies" (1987)
- NIST SP 800-53 — Security and Privacy Controls
- NIST SP 800-92 — Guide to Computer Security Log Management
End of document.