Blocked Before They Start. Encrypted Before They Finish.
Two Walls of Defense: Kernel Gate + Sub-5ms Hardware Encryption
OpenDLP intercepts every file access at the operating system kernel — the deepest layer where all reads, writes, and moves pass through. Wall 1: The AUTH_OPEN kernel gate blocks untrusted processes from even opening vault files. Zero bytes delivered. Wall 2: Files are encrypted with hardware-bound AES-256 keys in 3–5ms — faster than any network exfiltration can complete. The decryption key exists only on a physical security token — never in software, never on the network.
1. Two Walls of Defense: Why Both Matter
Most DLP products focus on one thing: detecting exfiltration and trying to block it. OpenDLP takes a fundamentally different approach with two independent defense layers, either of which is sufficient to prevent data loss.

Wall 1: AUTH_OPEN Kernel Gate
Every open() syscall passes through the kernel. OpenDLP intercepts it before the process gets a file descriptor. Untrusted processes are denied immediately. The attacker’s read(), mmap(), cp, and cat all fail with “Operation not permitted.”
Result: 0 bytes delivered. Speed is irrelevant — the door never opens.
Wall 2: Sub-5ms Hardware Encryption
Even if Wall 1 is bypassed (trusted process compromise, ESF crash), files are AES-256-GCM ciphertext within 3–5ms. The fastest network exfiltration (LAN netcat) takes 10ms+ for a 1MB file. The attacker loses the race and gets gibberish.
Result: Ciphertext only. Decryption requires a physical YubiKey.
Why Both Walls Are Necessary
| Attack Type | Wall 1 (AUTH_OPEN) | Wall 2 (Encryption) | Result |
|---|---|---|---|
| Local read (cat, cp, read()) | BLOCKED — open() denied | Backup if bypassed | 0 bytes |
| Python/mmap local read | BLOCKED — open() denied | Backup if bypassed | 0 bytes |
| LAN netcat exfil (1MB) | BLOCKED — open() denied | Encrypted in 4.7ms, exfil takes 10ms+ | Ciphertext |
| SCP/rsync exfil | BLOCKED — open() denied | Encrypted in 4.7ms, exfil takes 80ms+ | Ciphertext |
| HTTPS POST (curl) | BLOCKED — open() denied | Encrypted in 4.7ms, exfil takes 250ms+ | Ciphertext |
| Trusted process compromise | ALLOWED (trusted) | Gets ciphertext | Ciphertext |
Encryption Performance Benchmarks (from ESF logs)
| File Size | Encryption Time | Throughput | Context |
|---|---|---|---|
| 39 bytes | 0.2ms | — | Config file, API key (fixed overhead dominates) |
| 42 bytes | 1.4ms | — | Small text document |
| 1.0 MB | 3.3ms | 303 MB/s | Typical sensitive document |
| 1.4 MB | 4.7ms | 298 MB/s | SQL database dump |
| 7.0 MB | 10.5ms | 667 MB/s | Spreadsheet with PII |
| 14.0 MB | 19.5ms | 718 MB/s | Large CSV export |
Why So Fast?
AES-256-GCM leverages hardware acceleration built into modern processors (ARMv8 Cryptography Extensions on Apple Silicon, AES-NI on Intel). The key wrapping step is a single 256-byte RSA operation computed in ~0.5ms. No network calls during encryption — only a pre-loaded public key is used.
2. System Architecture: Dual-Layer Kernel Protection
OpenDLP operates as a kernel extension that hooks into the operating system's virtual filesystem layer (VFS). Every userspace process that calls open(), rename(), or any file operation passes through VFS. There is no userspace bypass.

Dual-Client Design
The extension runs two independent monitoring clients, each with isolated processing queues to prevent interference:
Primary Client (AUTH)
Handles authorization events with strict kernel deadlines (~10 seconds). Makes real-time ALLOW/DENY decisions for every file access.
- AUTH_OPEN — Gate every file read/write
- AUTH_RENAME — Block plaintext leaving vault
- NOTIFY_CLOSE — Trigger encryption after write
Lifecycle Client (NOTIFY)
Handles informational events with zero deadline pressure. Monitors process launches, file deletions, and access patterns.
- NOTIFY_OPEN — Track vault file readers
- NOTIFY_EXEC — Detect exfil tools launched by readers
- NOTIFY_UNLINK — Evict cache on file delete
- NOTIFY_CREATE — Detect new files in vault
In-Memory Encryption Cache
A 4-state cache tracks every vault file's encryption status without touching the filesystem. Zero I/O inside authorization callbacks — decisions happen in microseconds from memory alone.
State Meaning AUTH_OPEN Response ─────────── ────────────────────────────── ────────────────────────── UNKNOWN File not yet scanned ALLOW + trigger encryption PLAINTEXT File confirmed unencrypted DENY read + trigger encryption PENDING Encryption in progress DENY read (race protection) ENCRYPTED File protected with AES-256 ALLOW (safe to read)
Key Design Principle: Respond First, Encrypt Later
The kernel authorization response is sent immediately (microseconds). Encryption happens asynchronously on a separate processing queue. This ensures the kernel deadline is never missed — the callback returns before encryption begins.
3. Kernel Access Control: Every open() Is Gated
Every open() syscall targeting a vault file passes through a decision tree. The kernel waits for our response before allowing the process to access the file. No process — local, remote, scripted, or malicious — can bypass this gate.

Authorization Decision Tree
1 Is the file in a protected vault?
No → ALLOW immediately (cached). Non-vault files are never impacted.
Yes → Continue to next check.
2 Is this our own extension process?
Yes → ALLOW (self-access for encryption operations).
No → Continue.
3 Is this a write-only operation?
Yes → ALLOW (writing new data is safe, marks file as PLAINTEXT for future encryption).
No (READ) → Continue to the critical check.
4 What is the file's encryption state?
- ENCRYPTED → ALLOW — file is safe, reader gets ciphertext
- PENDING → DENY — encryption in progress, block the race
- PLAINTEXT / UNKNOWN → Check if process is trusted
- Trusted → ALLOW + trigger async encryption
- Untrusted → DENY + trigger async encryption
Rename Escape Prevention
Attackers can try to move files out of the protected vault before encryption completes. OpenDLP's AUTH_RENAME handler blocks this:
- Source file in vault, destination outside vault, file still plaintext → DENY
- Source file in vault, destination outside vault, file encrypted → ALLOW (safe — they get ciphertext)
Result: Plaintext files physically cannot leave the vault. Only encrypted files may be moved outside.
4. Attack Simulations: What the Attacker Gets
We simulated real-world exfiltration attempts using common tools an attacker would use after compromising an endpoint. Every test was run against a vault containing files with simulated PII, API keys, and financial data.

1 Attack: Direct File Copy (cp)
$ cp /protected/vault/customer_PII.csv /tmp/stolen.csv
cp: Operation not permittedBLOCKED — Plaintext file denied at the kernel level. Zero bytes copied. The cp process never received a single byte of file content.
2 Attack: Read and Pipe (cat)
$ cat /protected/vault/secret_database.sql > /tmp/exfil.dat
cat: Operation not permittedBLOCKED — The output file contains zero data from the original. The kernel denied the read before cat could access any content.
3 Attack: Mass Copy of 50 Files
$ for i in $(seq 1 50); do cp record_$i.txt /tmp/stolen/; done
Result: 50 files copied (they were already encrypted)
$ hexdump -C /tmp/stolen/record_1.txt | head -1
00000000 4f 50 45 4e 44 4c 50 5f 50 49 56 5f 76 31 ... |OPENDLP_PIV_v1|NEUTRALIZED — All 50 files had been encrypted within 1 second of creation (0.2–4.7ms each). The attacker gets 50 files of AES-256 ciphertext. Without the physical security key, the data is permanently unrecoverable.
4 Attack: Rename Escape (Move Outside Vault)
$ echo "SSN: 123-45-6789" > vault/escape.txt [KERNEL] Encrypted in 1.3ms $ mv vault/escape.txt /tmp/stolen.txt Move allowed — file was already encrypted (1.3ms after creation)
NEUTRALIZED — The file was encrypted 1.3ms after creation. By the time the attacker's mv command executed, there was no plaintext to steal. The "stolen" file is useless ciphertext.
Origin Independence: Works Against All Sources
The kernel gate intercepts every userspace process, regardless of where the attack originates:
| Attack Source | How It Reaches Files | Intercepted? |
|---|---|---|
| Local terminal (bash, zsh) | open() syscall | YES |
| Remote SSH session | open() via sshd child | YES |
| Malware / RAT | open() syscall | YES |
| Python / Node.js script | open() via runtime | YES |
| Network file copy (scp, rsync) | open() on source | YES |
| GUI drag-and-drop | open() + rename() | YES |
5. Encryption Pipeline: Hardware-Bound Keys
Each file is encrypted with a unique, random File Encryption Key (FEK). The FEK is then wrapped using a hardware security key's RSA-2048 public key. Decryption requires physical possession of the security token — no software-only recovery is possible.
Cryptographic Components
| Component | Algorithm | Details |
|---|---|---|
| File Encryption | AES-256-GCM | 32-byte random FEK, 12-byte nonce, 16-byte auth tag |
| Key Wrapping | RSA-2048 OAEP (SHA-256) | FEK encrypted with hardware token's public key |
| Key Generation | CSPRNG | Cryptographically secure random, unique per file |
| File Write | Atomic rename | write(tmp) → rename(tmp, original) — no partial writes |
Encryption Flow
1 Generate unique key material
32-byte FEK + 12-byte nonce from hardware-seeded CSPRNG. Every file gets a unique key. Compromise of one file's key reveals nothing about others.
2 Encrypt file content
AES-256-GCM authenticated encryption. Provides both confidentiality (data hidden) and integrity (tamper detection). Hardware-accelerated on modern processors.
3 Wrap FEK with hardware key
RSA-2048-OAEP wraps the 32-byte FEK into a 256-byte blob using the security token's public key. The private key never leaves the hardware token — decryption happens on-chip.
4 Atomic write + ownership preservation
Encrypted content written to temp file, then atomically renamed over the original. File ownership and permissions preserved. No window where data is partially written.
Decryption: Physical Token Required
To decrypt any file, you need:
- Physical possession of the hardware security token
- PIN knowledge to authenticate to the token
- Physical touch of the token (touch-to-decrypt policy)
The FEK is unwrapped on-chip inside the hardware token. The private key never enters host memory. Even with full root access to the machine, the decryption key cannot be extracted.
6. Engineering Deep Dive: The 14-Version Investigation
The kernel extension suffered from a crash loop — it would start, run for ~15 seconds, then be killed by the kernel. No errors logged. No exceptions caught. The extension simply vanished. This section documents the systematic diagnostic process that identified the root cause.
The Symptom
Pattern: Extension starts → runs ~15 seconds → kernel kills with "Client did not respond to AUTH event within deadline" → system restarts it → crash loop repeats every 15 seconds.
Diagnostic Timeline: Binary Search Debugging
We used systematic component elimination — removing pieces until the crash stopped, then re-adding them one at a time to isolate the cause.
| Version | Change | Result |
|---|---|---|
| v25 | Added diagnostic counters | CRASHED |
| v26 | Removed lifecycle client entirely | CRASHED |
| v27 | Bare minimum callback — instant ALLOW, zero logic | CRASHED |
| v29 | Zero event subscriptions | STABLE — crash is event-related |
| v30 | Re-enabled AUTH_OPEN with instant ALLOW | CRASHED |
| v33 | Nuclear minimal — no init, no cache, no PIV | STABLE at idle, CRASHED on activity |
| v35 | Muted 16 system processes | CRASHED |
| v36 | Changed response API + muted all processes | STABLE (but zero events) |
| v37 | Kept new response API, unmuted all processes | STABLE with 104+ events |
| v38 | Full logic restored with new API | PRODUCTION — 20+ min stable |
Root Cause: Wrong Kernel Response API
The kernel's file-open authorization event is unique among all authorization events. Instead of a binary ALLOW/DENY, it expects a bitmask of allowed file access flags (read, write, append, etc.). We were using the binary response API. The kernel couldn't interpret the response, treated it as "no response," and killed the extension after the deadline.
The fix was a single line of code. Changed the response function to send a flags bitmask instead of a binary result. The extension went from crashing every 15 seconds to running indefinitely.
Lessons Learned
- Systematic elimination beats hypothesis chasing — 14 versions of methodical component removal found what debugging couldn't
- Separate authorization and notification clients — strict deadline events must be isolated from deadline-free events
- Zero filesystem I/O in authorization callbacks — any file read inside a file-open handler creates recursive events
- Respond instantly, process asynchronously — encryption happens after the kernel response, not before
7. Complete Test Results: 9/9 Passed
All tests performed on April 13, 2026, on production build v38, under real workload conditions including system indexing bursts and concurrent file operations.
| # | Test | Result | Details |
|---|---|---|---|
| 1 | New file auto-encryption | PASS | File encrypted in 1.4ms after creation |
| 2 | Large file encryption (682KB) | PASS | 1.5ms encryption time |
| 3 | Large file encryption (14MB) | PASS | 19.5ms, 718 MB/s throughput |
| 4 | 20-file rapid burst | PASS | 20/20 encrypted in same second |
| 5 | 50-file rapid burst | PASS | 50/50 encrypted, all verified |
| 6 | Startup vault sweep | PASS | 28 plaintext files caught and encrypted on launch |
| 7 | Hardware key round-trip decrypt | PASS | FEK unwrapped on-chip, original plaintext recovered |
| 8 | Unknown process detection | PASS | Untrusted process flagged accessing vault |
| 9 | Long-term stability (20+ min) | PASS | Hundreds of AUTH events, heavy activity, zero crashes |
Stress Test: 50-File Simultaneous Burst
Files created: 50 (simultaneously via shell loop) Files encrypted: 50 (0.2ms - 4.7ms each) Total time: < 1 second Files leaked: 0 Verification: Every file starts with encrypted header. Attacker result: 50 files of AES-256 ciphertext.
Decryption Round-Trip Verification
End-to-End Crypto Verified
- Parsed encrypted file header → extracted wrapped FEK, nonce, ciphertext
- Hardware token unwrapped 32-byte FEK on-chip (key never exposed to host)
- AES-256-GCM decrypted ciphertext using unwrapped FEK
- Original plaintext recovered — byte-for-byte identical
Conclusion: The encryption is real, the decryption works, and the key is hardware-bound. This is not security theater — it's mathematically proven protection.
8. Traditional DLP vs Kernel-Level Encryption

Traditional DLP
- Monitor — Watch network traffic or file operations
- Detect — Pattern match for sensitive data
- Alert — Send notification to SOC
- Block — Attempt to prevent transfer
- Hope — Hope the attacker didn't already read the data
If the block fails or is bypassed, data is fully exposed in plaintext. The attacker had seconds to minutes of access.
OpenDLP Kernel Encryption
- Wall 1: Gate — Kernel blocks untrusted open() calls instantly
- Wall 2: Encrypt — 3–5ms, hardware-bound AES-256 keys
- Deny or serve ciphertext — Attacker never sees plaintext
- Log — Full forensic trail of every access attempt
- Done — Data was never exposed. No “hope” step.
There is no "hope" step. The data is encrypted before the attack can physically complete. The key doesn't exist in software.
Protection Layer Comparison
| Protection | Traditional DLP | OpenDLP Kernel |
|---|---|---|
| Operates at | Network / application layer | Kernel VFS layer |
| Response time | Seconds to minutes | Instant (AUTH gate) + 3–5ms (encrypt) |
| Can be bypassed by | VPN, encrypted tunnel, USB | Nothing in userspace |
| Attacker gets plaintext? | Yes, during detection window | Never |
| Key storage | Software keychain / HSM | Physical token only |
| Works against SSH/SCP? | Often invisible | Intercepted at kernel |
| Works against insider? | Can disable agent | Kernel extension, requires admin + reboot |
| Offline protection? | Requires network | Fully local, no network needed |
9. Conclusion
On April 13, 2026, OpenDLP demonstrated a two-wall defense that makes data exfiltration functionally impossible. Wall 1 (AUTH_OPEN) blocks untrusted processes from even opening vault files — the attacker’s read(), mmap(), cp, and cat all fail at the kernel level with zero bytes delivered. Wall 2 (encryption) ensures that even if Wall 1 is somehow bypassed, files are AES-256-GCM ciphertext within 3–5ms — faster than any network exfiltration method can complete.
The protection operates at the deepest layer of the operating system. Every open() syscall passes through the kernel gate. No userspace process — local or remote, legitimate or malicious — can bypass it. Attackers who attempt direct file reads are denied at the kernel level. Attackers who copy already-encrypted files get AES-256 ciphertext that requires a physical hardware token to decrypt.
The encryption key never exists in software. It lives inside a hardware security token, is used for on-chip decryption only, and requires physical possession plus PIN knowledge plus physical touch. There is no software backdoor, no key escrow, no recovery mechanism without the physical token.
What We Proved
- AUTH_OPEN kernel gate — blocks all untrusted reads instantly
- 3–5ms encryption — faster than network exfil (10ms+ minimum)
- 50-file burst encrypted in under 1 second
- 14MB file encrypted in 19.5ms (718 MB/s)
- Direct reads: kernel DENY, zero bytes leaked
- Mass copy: 50 files of useless ciphertext
- Rename escape: file encrypted before move completes
- Hardware key round-trip decrypt verified
- 20+ minutes stable under heavy load
- Origin-agnostic: works against all attack sources
What's Next
- Multi-device ACL deployment (Windows, Linux, Pi)
- Enterprise-scale testing with 100+ protected files
- SIEM/SOAR integration for forensic event forwarding
- Screen recording and clipboard detection
- Cross-platform kernel monitoring (Windows minifilter)
- Compliance reporting (SOC 2, HIPAA, PCI-DSS)
