Skip to content
🔧 Developer beta launches 30 September 2026 — free for developers. Flash your own ESP32-S3 and test it. Learn more →

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

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.

  • ✅ Full SDK source (commit c9f9abb)
  • ✅ Compiled binaries
  • CRYPTIN_PROTOCOL.md
  • CRYPTIN_FILE_FORMAT.md
  • ✅ Real .crin sample files
  • ✅ Dongle A4CB8FF84948 via USB passthrough
  • ✅ Admin access to the PC
  • ✅ Internet access for tools
  • ✅ DnSpy, USBPcap, WinPmem, x64dbg
  • ✅ Python with hidapi
  • ❌ Firmware source (closed source)
  • ❌ lake8.dev RSA private key
  • ❌ Dongle PIN
  • ❌ 24-word seed phrase
  • ❌ License server access

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

Every finding must be:

  1. Verified on real hardware HW-NOW
  2. With exact response bytes
  3. With chip serial and timestamp
  4. Signable: “verified on firmware [hash] on chip [serial]

Findings marked NON-VERIFIED are explicitly excluded from the signature.

“With full source code, DnSpy, admin access, and the physical dongle but no PIN — does a .crin file 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.

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 ✅

F-001-REV — Shared unlock session → online 0x0E decrypt oracle

Section titled “F-001-REV — Shared unlock session → online 0x0E decrypt oracle”

High Confirmed — open (session binding fix: Q2 2027, with the WiFi firmware release) HW-NOW Sessions 1, 2, 3

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_OK
0x0E DECRYPT_BLOCK on docx.crin → 27,206 bytes
magic: 50 4b 03 04 (PK ZIP header)
GCM tag: VERIFIED on-chip
Key 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”

Medium Confirmed — fail-closed HW-NOW Session 3

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) → OK
h2 chunk1 (attacker) → OK (accepted in h1 transfer)
ascending interleave h1/h2 → tag VERIFIED
plaintext == mix(victim + attacker data)
[race] h1 seq1 OK ; h2 seq1 → SEQ_ERROR
one out-of-seq chunk DESTROYS the transfer

Impact. Two distinct effects:

  1. A valid tag over interleaved data — chunks from two handles in ascending sequence merge into a single authenticated stream.
  2. Trivial denial of service — a single out-of-sequence 0x0D chunk 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”

Low Confirmed HW-NOW Sessions 1, 2, 3

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 s
Zero 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”

Info Mitigated HW-NOW Sessions 1, 3

Before the fix (session 1):

PinForm used: var pin = _pin.Text; // immutable .NET string
RAM scan after unlock: PIN present as UTF-16LE
5 copies, lingering ~1–2 minutes until GC.

After the fix (session 3):

PinForm now uses: _pin.Text.ToCharArray()
VerifyPin(ReadOnlySpan<char>) overload
Array.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”

Info Remediated ✅ HW-NOW Sessions 2, 3

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 ceiling

Fix: CMD_GET_LICENSE_STATUS (0x0C) placed behind the PIN gate.

0x0C at LOCKED dongle → 0xE1 ERR_PIN_REQUIRED
0x0C flood 500× → 0xE1 500/500, ~2.0ms median
Gate short-circuits before RSA: leak closed, DoS closed.

F-B — JWT accumulator inactivity timeout

Section titled “F-B — JWT accumulator inactivity timeout”

Info Remediated ✅ HW-NOW Session 3

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”

Info Remediated ✅ source-verified Session 2

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 appCryptinCipher 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”

Info Remediated ✅ HW-NOW Sessions 2, 3

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-check r[1]==opcode)
  • DrainIfPending() clears the queue after an exchange that never collected its reply
  • A Foreground() scope pauses _watch during 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.

HW-NOW Session 3

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 seq3SEQ_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”

Info HW-NOW Session 3

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 replug
0x04 → 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.

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.

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

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

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.