Security Assessment — August 2026
Red Team Report + Audit of Engagement
| Chip tested | A4CB8FF84948 |
| Firmware | dd14270 |
| Sessions | 3 (2026-08-02 / 2026-08-03) |
| Red team agent | Claude Opus 5 (Anthropic), released 24 July 2026 |
| Published | 2026-08-04 |
What we asked for
Section titled “What we asked for”We gave an AI red team agent (Claude Opus 5) full admin access to a Windows 11 VM, our complete SDK source code, compiled binaries, DnSpy, USBPcap, WinPmem, x64dbg, a live dongle, and sample encrypted files.
We told it: find every way to open a .crin file without the physical dongle.
Then find everything else. Then invent attacks we haven’t considered.
Material provided
Section titled “Material provided”- ✅ Full SDK source (commit
c9f9abb) - ✅ Compiled binaries
- ✅
CRYPTIN_PROTOCOL.md - ✅
CRYPTIN_FILE_FORMAT.md - ✅ Real
.crinsample files - ✅ Dongle
A4CB8FF84948via USB passthrough - ✅ Admin access to the PC
- ✅ Internet access for tools
- ✅ DnSpy, USBPcap, WinPmem, x64dbg
- ✅ Python with
hidapi
Material NOT provided
Section titled “Material NOT provided”- ❌ Firmware source (closed source)
- ❌ lake8.dev RSA private key
- ❌ Dongle PIN
- ❌ 24-word seed phrase
- ❌ License server access
Authorized perimeter
Section titled “Authorized perimeter”Allowed:
- ✅ Any action on this PC
- ✅ Download any tool
- ✅ Intercept any process
- ✅ Open raw HID handles
- ✅ Replace DLLs
- ✅ Request a live Crypt-in session (user enters PIN while agent listens)
Out of bounds:
- ❌ Other PCs on the network
- ❌
license.lake8.dev - ❌ Consume PIN attempts without explicit authorization
- ❌ Burn chip eFuses
Signing protocol
Section titled “Signing protocol”Every finding must be:
- Verified on real hardware HW-NOW
- With exact response bytes
- With chip serial and timestamp
- Signable: “verified on firmware
[hash]on chip[serial]”
Findings marked NON-VERIFIED are explicitly excluded from the signature.
The main question
Section titled “The main question”“With full source code, DnSpy, admin access, and the physical dongle but no PIN — does a
.crinfile open?”
At rest: NO. The secret lives in silicon. Sources, DnSpy and admin access don’t help.
During an unlock window: YES — but only via an online oracle (0x0E), not
offline. No permanent compromise. Session closed = secure.
Findings summary
Section titled “Findings summary”| ID | Severity | Status | Condition |
|---|---|---|---|
| F-001-REV | High | Confirmed | Compromised PC + active unlock |
| F-ISO | Medium | Confirmed (fail-closed) | Compromised PC + active unlock |
| F-003/B1 | Low | Confirmed | Compromised PC |
| F-NEW-RAM | Info | Mitigated | Compromised PC + unlock |
| F-004 | Info | Remediated ✅ | — |
| F-B | Info | Remediated ✅ | — |
| F-FRAME | Info | Remediated ✅ | — |
| F-SYNC | Info | Remediated ✅ | — |
Finding detail
Section titled “Finding detail”F-001-REV — Shared unlock session → online 0x0E decrypt oracle
Section titled “F-001-REV — Shared unlock session → online 0x0E decrypt oracle”Condition required: compromised PC + active unlock window.
Action. The user unlocks the app with the correct PIN. A second unauthenticated HID handle is opened from a separate process, without sending a PIN.
0x04 GET_SECRET from 2nd handle → 0x00 STATUS_OK0x0E DECRYPT_BLOCK on docx.crin → 27,206 bytesmagic: 50 4b 03 04 (PK ZIP header)GCM tag: VERIFIED on-chipKey never on the bus.What session 1 claimed, and why it was wrong. Session 1 concluded that
file_secret → HKDF → AES-GCM gave offline decryption forever. Session 2
disproved it: file_secret does not decrypt .crin files offline. 279 key ×
AAD variants were tried against known plaintext, with no match.
The real vector is the 0x0E on-chip decrypt oracle reachable from a shared
session. Online only. It requires the chip present and unlocked.
Unlock window, measured in session 3:
| Idle time | State |
|---|---|
| 300 s | still UNLOCKED |
| 600 s | LOCKED |
The threshold sits between 5 and 10 minutes.
The detail that matters: every HID command resets the idle timer. An active attacker polling the session keeps it open indefinitely. Auto-lock only protects a chip that is genuinely idle.
Safe closure. Unplug the dongle and wait at least 5 seconds. A fast replug may not fully power-cycle the ESP32 — capacitors can keep it powered briefly, preserving the unlock state. See closing the security window.
Fix: session binding (handle/process authentication), Q2 2027, with the WiFi firmware release.
F-ISO — Shared block-cipher accumulator, no handle binding
Section titled “F-ISO — Shared block-cipher accumulator, no handle binding”Condition required: compromised PC + active unlock window.
Action. Two OS handles on the same HID device. Handle 1 (victim) starts a
0x0D encrypt transfer; handle 2 (attacker) interleaves chunks in ascending
sequence.
[interleave] h1 chunk0(total=6) → OKh2 chunk1 (attacker) → OK (accepted in h1 transfer)ascending interleave h1/h2 → tag VERIFIEDplaintext == mix(victim + attacker data)
[race] h1 seq1 OK ; h2 seq1 → SEQ_ERRORone out-of-seq chunk DESTROYS the transferImpact. Two distinct effects:
- A valid tag over interleaved data — chunks from two handles in ascending sequence merge into a single authenticated stream.
- Trivial denial of service — a single out-of-sequence
0x0Dchunk from a second handle immediately aborts any encrypt or decrypt in progress.
Fail-closed, confirmed. The victim aborts, because its tag over the truncated stream does not verify. There is no silent poisoning of a file the victim would go on to accept.
Root cause, shared with F-001-REV: the block-cipher accumulator is global to the chip, not bound to a handle or a session.
Fix: session binding — same milestone as F-001-REV.
F-003/B1 — PIN in cleartext on the USB bus, replayable
Section titled “F-003/B1 — PIN in cleartext on the USB bus, replayable”Condition required: compromised PC (USB sniffing).
Action. USBPcap capture running during a legitimate user unlock. The PIN is extracted from the pcap and replayed against a locked chip.
Report OUT (endpoint 0x01):01 08 06 36 35 38 38 30 35= [report_id=01][opcode=0x08][len=6][658805 as ASCII]PIN at offset 3, 6 bytes, cleartext.Session 3 — fully automated pipeline (f003_auto.py):
(1) PIN from pcap: 0.2 ms(2) Replay 0x08 → UNLOCKED: 3.1 ms(3) 0x0E oracle → PDF plaintext 182,958 bytes, %PDF, tag VERIFIED: 6.66 s
TOTAL capture → full plaintext: 6.73 sZero human interaction after capture.There is no anti-replay mechanism on CMD_VERIFY_PIN (0x08). The PIN is
static, derived from the seed via HKDF, so a capture stays valid indefinitely
until the seed changes.
Fix: challenge-response for VERIFY_PIN — the client sends
HMAC(PIN, nonce) instead of the raw PIN. Q2 2027, with the WiFi firmware
release.
F-NEW-RAM — PIN transiently in process RAM
Section titled “F-NEW-RAM — PIN transiently in process RAM”Before the fix (session 1):
PinForm used: var pin = _pin.Text; // immutable .NET stringRAM scan after unlock: PIN present as UTF-16LE5 copies, lingering ~1–2 minutes until GC.After the fix (session 3):
PinForm now uses: _pin.Text.ToCharArray()VerifyPin(ReadOnlySpan<char>) overloadArray.Clear() in finally block immediately after exchange.Verified on hardware: the new path unlocks correctly from LOCKED with the correct PIN, with zero attempts consumed.
Declared residual limit. TextBox.Text materialises a string by
construction, and the control keeps its own internal buffer. Zero PIN
occurrences in RAM is not achievable from application code alone. The fix
reduces exposure; it does not eliminate it — and the code comment says exactly
that: “reduction, NOT elimination.”
F-004 — License status leak pre-auth via 0x0C
Section titled “F-004 — License status leak pre-auth via 0x0C”Before the fix:
0x0C GET_LICENSE_STATUS responded pre-auth:→ Revealed license validity and expiry date→ RSA-4096 verification on every call (~90ms)→ Pre-auth DoS surface: ~11 req/s ceilingFix: CMD_GET_LICENSE_STATUS (0x0C) placed behind the PIN gate.
0x0C at LOCKED dongle → 0xE1 ERR_PIN_REQUIRED0x0C flood 500× → 0xE1 500/500, ~2.0ms medianGate short-circuits before RSA: leak closed, DoS closed.F-B — JWT accumulator inactivity timeout
Section titled “F-B — JWT accumulator inactivity timeout”Session 2 reported a false negative. The test used a 6-second idle against a 30-second threshold and concluded the timeout was absent. The measurement was sound; the threshold it was tested against was wrong.
Session 3 verification:
A: seq0 → 6s idle → seq1 → STATUS_OK ✅ (reproduces Session 2 measurement — correct behavior)
B: seq0 → 31.5s idle → seq1 → 0xF8 ERR_SEQ_ERROR ✅ (timeout fires at 30s as declared)Design note. JWT_TIMEOUT_MS is 30,000 and BLOCK_TIMEOUT_MS is 5,000. The
distinction is intentional: a license handover may wait on the network
mid-activation, while a file encrypt should never pause mid-chunk.
F-FRAME — Incompatible dual wire framing in the public SDK
Section titled “F-FRAME — Incompatible dual wire framing in the public SDK”Before the fix:
SendChunk() (app path via WriteCrin/ReadCrin):seq 3B + total 3B, no length byte ✅ (correct)
Block() public methods(EncryptBlock/DecryptBlock(byte[],byte[])):seq 2B + total 2B + explicit length byte ❌(reintroduces the bug the other paths document as fixed)There were zero callers in the app — CryptinCipher always used the
SendChunk path. Block() offered a broken API to any external caller.
Fix: Block() and the (byte[],byte[]) overloads were removed. A comment at
the removal site documents why re-framing is not the fix: the signatures do not
match — a missing nonce parameter for decrypt, and the wrong salt ownership for
encrypt.
F-SYNC — Timer concurrency in dongle access
Section titled “F-SYNC — Timer concurrency in dongle access”Root cause. The PollDongle() timer (1 s) issued HID commands concurrently
with foreground operations. A reply nobody reads stays queued, and the next
Read() takes it — leaving the exchange permanently off by one.
Reported symptom: “echoes 0x08 expected 0x03” during file encryption.
Fix:
Read()skips stale reports (echo-checkr[1]==opcode)DrainIfPending()clears the queue after an exchange that never collected its reply- A
Foreground()scope pauses_watchduring any foreground dongle operation
Verified in session 3: a file larger than 1 MB encrypted with zero echo
mismatches, and probe_report_desync.py passing.
Attacks that did not work
Section titled “Attacks that did not work”A report that only lists what worked is a sales document. These were tried and held.
| Attempt | Result |
|---|---|
0x0E with substituted header pubkey |
Decrypts anyway (tag 0x00) — see below |
0x0E with tampered nonce/salt |
0xF6 ERR_TAG_MISMATCH ✅ GCM integrity holds on-chip |
| Cross-transfer tag reuse | 0xF6 ✅ the tag is bound to its data |
0x0B with foreign JWT (false RS256 signature) |
0xF1 ERR_INVALID_JWT ✅ |
Power-cycle during an active 0x0D transfer |
Clean reset: LOCKED, accumulator gone; old seq3 → SEQ_ERROR on a fresh transfer; fresh encrypt after power-cycle OK ✅ fail-safe, no residue |
| F-WRAP — counter wraparound hypothesis | Encrypted ~3.63 MB = 66,000 chunks (> 65,536); round-trip identical, tag VERIFIED ✅ firmware counters are ≥ 3 bytes. The wraparound described in old comments was historical and has been corrected |
The first row is not a failure of the chip: the “wrong dongle” check is host-side only, and that is declared. Security depends on the physical dongle, not on the identity claimed in a file header — see dongle binding.
Observation — a fast replug may preserve unlock
Section titled “Observation — a fast replug may preserve unlock”A very fast unplug/replug may not fully power-cycle the ESP32. Internal capacitors can keep it powered briefly, preserving the unlock state.
0x04 → STATUS_OK (UNLOCKED) after fast replug0x04 → 0xE1 (LOCKED) after deliberate power-cycle (≥5 seconds unplugged)User guidance. “Unplug the dongle” closes the F-001-REV window only if the dongle stays unplugged long enough to fully power down. Wait at least 5 seconds.
What this report does not cover
Section titled “What this report does not cover”All open findings require:
- The user’s PC to be already compromised
- Active malware during the session
- Access to the USB bus or to processes
On an uncompromised PC, none of the open findings are exploitable.
If your PC is compromised, the attacker already has access to everything — files in plaintext, memory, processes. The dongle is not the problem.
This is declared in our threat model from day one.
Fix timeline
Section titled “Fix timeline”September 2026 — developer beta
- All remediated findings in
main - Open findings declared
- This report published
Q1 2027 — public launch
- Session 4 red team
- Updated report
Q2 2027 — WiFi firmware release
- Session binding (F-001-REV)
- Challenge-response
VERIFY_PIN(F-003)
Both ride the same firmware release, and both land after the public launch. F-001-REV and F-003 are therefore open at launch, with the mitigations declared in their sections above.
Session 4 — the developer beta itself
Beta participants are the red team. Findings from the developer beta feed into the Q1 2027 public launch report.
Network Edition — planned, no date confirmed
- Secure protocol for networked deployments
Signature
Section titled “Signature”Everything in this report marked HW-NOW
was verified on real hardware during the indicated sessions, on firmware
dd14270, on chip A4CB8FF84948.
No PIN attempts were consumed with an incorrect PIN.
No ACK_PROVISIONING was sent.
No eFuses were burned during the test sessions — meaning no additional eFuses beyond those already burned at provisioning.
The test dongle (A4CB8FF84948) was provisioned before the sessions began:
Secure Boot v2 active, Flash Encryption active, eFuses burned — identical to a
production unit. That is mandatory for all Crypt-in dongles, beta and production
alike. No unburned dongle was used in any test.
lake8.dev — August 2026
Terms of ServicePrivacy PolicySecurity contact: security@lake8.devVulnerability Disclosure Policy
Alcuni contenuti sono stati redatti con il supporto di strumenti di intelligenza artificiale generativa e revisionati dall'autore. Le immagini hardware hanno scopo puramente illustrativo.
Some content was drafted with the support of generative AI tools and reviewed by the author. Hardware images are purely illustrative.
Einige Inhalte wurden mit Unterstützung generativer KI-Werkzeuge verfasst und vom Autor überprüft. Hardware-Abbildungen dienen ausschließlich illustrativen Zwecken.
Algunos contenidos han sido redactados con el apoyo de herramientas de IA generativa y revisados por el autor. Las imágenes de hardware tienen carácter meramente ilustrativo.
In caso di conflitto tra versioni linguistiche, prevale il testo in lingua italiana.