# Phantomdrive: a USB drive that hides an encrypted partition behind a decoy one

> Phantomdrive is open source firmware for a CH569W based USB drive that exposes a plausible looking partition while the real data sits encrypted behind a password file, with AES-256 in XTS or CTR mode and PBKDF2 key derivation done in hardware. The project is explicit that it has not been professionally audited and that it is experimental, and it is interesting as much for the mechanism as for that warning.

**o7-machinehum/phantomdrive** — Seemingly normal USB drive with a hidden security feature

- Repository: https://github.com/o7-machinehum/phantomdrive
- Stars: 888 · Forks: 62
- Language: C
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/o7-machinehum-phantomdrive

## The unlock is a file whose content is the password

The mechanism is described in the opening paragraph and it is unusual enough to be worth reading twice.

Phantomdrive is an open source encrypted USB drive with a stealth mechanism to hide its second partition. To decrypt it you must create a file containing your password in the format password:addpasswordhere, and this is used to derive AES-XTS keys. The drive automatically unmounts itself, remounts the remaining disk, and encrypts and decrypts in place.

The unlock detection is content-based, and the documentation is careful to say so. While the device is locked, any write data containing the string password: can be interpreted as an unlock attempt, and the file does not need to be named unlock.txt.

That single detail shapes the whole user experience. There is no unlock application, no button combination, no challenge-response dialogue on the host. You write a file with a known prefix and the firmware notices. The device does the rest: it unmounts the decoy, remounts what is left of the disk, and switches between encrypted and decrypted views.

The consequence for the user is that a stray file, a shell redirect, a text editor autosave or a swap file could contain the magic string and trigger an unlock attempt. The documentation says this directly, tells you to be aware that shells save commands in history, and advises disabling the caching feature in any text editor you use to enter the password.

It also means the password file itself is the attack surface. If the host is compromised, the file that unlocks the drive may be sitting in a temp directory, in a shell history, or in an editor backup. The drive's crypto is not what protects the password in that scenario, the host is.

## The decoy is a capacity lie, and the size is a build parameter

The stealth mechanism is implemented in terms of capacity, and the build system makes the number an explicit choice.

The AES mode, the PBKDF2 iteration count and the locked capacity can be selected independently. LOCKED_SECTORS sets the locked, decoy, capacity in 512-byte sectors and defaults to 16,777,216 sectors, which is 8 GiB. It must be smaller than the physical card's sector count, otherwise the unlocked capacity calculation underflows.

The underflow warning is a real engineering constraint rather than a formality. The unlocked capacity is computed as the physical capacity minus the locked capacity, and if you ask for more locked sectors than the card has, that subtraction wraps around to a huge number and the firmware believes it has a capacity it does not have.

So the physical card is larger than what it appears to be. A user inserting the drive sees a partition of LOCKED_SECTORS sectors, which on the default build is 8 GiB, and the remainder of the card is where the encrypted data actually lives. The decoy is not a fake directory full of innocuous files, it is a real capacity that the drive presents and that the firmware protects.

Setting the locked capacity to zero would presumably defeat the mechanism entirely, and setting it too high breaks the arithmetic. In practice the value is a compromise between a decoy big enough to look plausible and the space you are willing to give up.

The repository layout reinforces that this is a hardware project rather than a software library. There is an ee directory for hardware files, a mech directory, a ref directory for reference docs, a test directory for testing the crypto components, a tests directory for verification scripts, a src directory for the firmware, a wch-ch56x-bsp board support package and a wch-ch56x-isp directory for the programming software.

## The crypto choices are real, and they are baked into the on-disk format

The security notes section is where the project states what it actually does, and it is more specific than most hobby hardware documentation.

The encrypted area can use AES-256-CTR or AES-256-XTS, with keys derived using PBKDF2-HMAC-SHA256 and either 100,000 or 600,000 iterations. The AES mode and KDF iteration count affect the on-disk format, so firmware built with different settings cannot decrypt the same data without migration or reformatting.

That last sentence is the operational trap. The three parameters are compile-time constants, not runtime settings, and the Makefile enforces them at build time with explicit error checks: AES_MODE must be CTR or XTS, and KDF_ROUNDS must be 100000 or 600000. The two are independent, which is why the release script produces four separate builds.

The four builds are written to build/CTR_100K, build/CTR_600K, build/XTS_100K, and build/XTS_600K. So the naming is the matrix, and the physical drive you end up with has a fixed format. If you flash a device with XTS and 600,000 rounds and later decide you want CTR with 100,000, the data on that device is unreadable by the new firmware, and there is no migration path described in the documentation.

On the choice between the two modes, the documentation states that AES-XTS is the default in the build and that CTR is the alternative, and the release script builds both. The security notes do not explain why one is preferred, which is a real gap for anyone choosing deliberately, since XTS is the mode designed for disk encryption and CTR is the mode that does not itself provide per-sector tweak resistance.

The KDF side is where the iteration count matters, and 100,000 is the default with 600,000 available. The firmware takes the iteration count as a compile-time define, in the form of a KDF_ROUNDS value suffixed with U to mark it as unsigned.

## Building it needs a patched RISC-V toolchain and a USB programmer

The build instructions are specific enough that they tell you this is not a project you clone and press enter on.

First the submodules and the ISP tool:

```bash
git submodule update --init --recursive --checkout --force
cd wch-ch56x-isp
make # Build the ISP tool
```

Then the toolchain, and the README is precise about why. The HydraUSB3 project requires the patched HydraUSB3 xPack GCC 12.2.0-1, which supports the WCH-Interrupt-fast interrupt attribute:

```bash
cd /tmp
curl -sL -o riscv-gcc-xpack.tar.gz \
  "https://github.com/hydrausb3/riscv-none-elf-gcc-xpack/releases/download/12.2.0-1/xpack-riscv-none-elf-gcc-12.2.0-1-linux-x64.tar.gz"
tar xzf riscv-gcc-xpack.tar.gz
sudo cp -r xpack-riscv-none-elf-gcc-12.2.0-1/* /usr/local/
```

The interrupt attribute detail is the kind of thing that would otherwise cost a day. The stock upstream RISC-V GCC will not compile this firmware, because the vendor's interrupt controller needs a non-standard attribute, and the HydraUSB3 project publishes a patched compiler for it.

The Makefile confirms the shape of the build. COMPILER_PREFIX is riscv-none-elf by default, and the architecture flags are selected from it, giving -march=rv32imac_zicsr for the elf prefix, with a separate riscv32imac for the older embed prefix. There is also a riscv64-elf option, and an unknown prefix is a hard error rather than a silent default.

Debugging is a single variable, make UART=1, which adds a DEBUG define. The board support package is a submodule at wch-ch56x-bsp, the board file is hydrausb3_v1.c, and the USB layer is built from the usb_devbulk driver sources.

## Flashing the drive requires holding a button while plugging it in

The build and flash sequence is documented with the physical step first, which is the tell that this is real hardware work.

```bash
# Remove flash drive
# While holding boot button, plug in

# Build and flash AES-XTS firmware with 600,000 PBKDF2 iterations and an
# 8 GiB locked capacity
make AES_MODE=XTS KDF_ROUNDS=600000 LOCKED_SECTORS=16777216 flash

# Or build and flash AES-CTR firmware with 100,000 iterations
make AES_MODE=CTR KDF_ROUNDS=100000 flash

# Build all four AES/KDF combinations
./release.sh
```

The sequence requires removing the flash drive and plugging it in while holding the boot button, which puts the CH569W into its programming mode so the ISP tool can talk to it over USB. The device is programmable over USB using the wch-ch56x-isp library, and the ISP tool itself is the first thing the build produces.

The hardware is the CH569W SoC, which has USB3, SDIO and an AES hardware block. That AES block is the reason the crypto is viable: the key derivation and the block encryption run in the silicon rather than in a software implementation on an MCU.

There is a separate release path for the hardware itself, the scripts/release_hardware.sh script, and a build.yaml at the top level of the repository, so there is more than one artifact being produced here: the firmware, the programmer, and something for the physical board.

For unlocking, the documentation gives two ways, and comments on both. One is a shell redirect, with a comment noting that this will keep your password in bash history but is fine for testing or insecure use:

```bash
# This will keep your password in bash history, but fine for testing/insecure
sudo echo "password:YourPasswordHere13245" > /mnt/unlock.txt
```

The other avoids the shell history by using dd and typing the password on standard input.

## The project tells you not to trust it with your data

The security notes open with a paragraph that is worth reading in full, because it defines the boundaries of the project.

This code has not been professionally audited. Treat Phantomdrive as an experimental open source hardware and firmware project rather than a formally reviewed security product. The current level of validation is described in the test README. And the author is not responsible for loss of data, security incidents, or other damage resulting from use of this project.

For a project whose entire purpose is to protect data at rest, that is an unusual amount of candour. Most security-adjacent hardware projects say some version of use at your own risk. This one says three specific things: no professional audit, experimental rather than reviewed, and a test README that describes what has actually been validated.

The distinction between the last two matters. An unaudited project that has been heavily tested is a different proposition from one that has been neither, and the documentation points you at the test directory, which is in the repository listing as the place to test the crypto components. There is also a tests directory for verification scripts, and a test directory in the tree for the crypto tests, so the verification work is at least visible.

There is also a document-level contradiction worth naming. The project describes a hidden second partition, and the security model is that a user with the password gets the data. But the unlock trigger is any write containing the string password:, and the password file may be sitting in a temp directory, a shell history or an editor swap file. A drive whose threat model is a lost or borrowed device is well served by this. A drive whose threat model is a compromised host is not, because the host already has a copy of the key material in some form.

The MIT licence covers the code, the copyright line is not stated in the README, and the LICENSE file in the repository root is the reference. Whether MIT is the right choice for a device whose entire function is to hide data is a question for whoever put the hardware together, not for this page.

## Where Phantomdrive is the wrong choice

Three situations rule it out, and one of them is the project itself telling you so.

The first is carrying data you cannot afford to lose. The disclaimer says the author is not responsible for loss of data or security incidents, and the format is not portable. Because the AES mode and the PBKDF2 iteration count are compiled into the firmware and determine the on-disk format, a drive flashed with one combination cannot be read by a device flashed with another, and the documentation states plainly that changing them requires migration or reformatting. There is no migration path described, and no recovery key.

The second is anything that requires a normal, boring USB drive. The point of the device is that it presents a decoy capacity and hides the rest, and that behaviour is exactly what breaks a workflow. A drive that unmounts itself when a file with a particular string is written to it is not a drive you can safely use with an automated backup tool, a syncing client, or a file manager that scans the volume.

The third is a build you cannot complete. The toolchain is a patched RISC-V GCC that has to be fetched from the HydraUSB3 project and copied into /usr/local, the programming tool is a separate module, and the flashing step needs a physical button held at the right moment. That is a high bar compared with a software library, and it means the audience is people who already build firmware for this class of hardware.

The reasonable alternative for most people is not another covert USB project but a plain encrypted container on an ordinary drive, which gives the same protection of data at rest without the decoy, without the hardware, and without the format lock. What Phantomdrive adds is physical plausibility: a device that looks like an 8 GiB drive and holds something else. That is a different requirement, and only worth pursuing for a specific reason.

## Conclusion

Phantomdrive fits a hardware developer who wants to build a data-hiding USB device from a CH569W board and is prepared to own the security risk, since the AES block, the firmware and the verification scripts are all in the repository. It is the wrong choice for anyone carrying data they would be unwilling to lose, because the project states that it has not been professionally audited and that the author is not responsible for data loss or security incidents, and it is the wrong choice if you need the firmware to be interoperable across devices, because the AES mode and iteration count change the on-disk format. Before you build it, read the test README to see what has actually been validated, and pick the AES mode and PBKDF2 count once, because changing either means reformatting.

## FAQ

### How does Phantomdrive hide its encrypted partition?

It creates a locked decoy capacity and keeps the real data encrypted behind it, using AES-256 in XTS or CTR mode with keys derived by PBKDF2-HMAC-SHA256. The decoy size is set by LOCKED_SECTORS, in 512-byte sectors, and defaults to 16,777,216 sectors, or 8 GiB.

### How do I unlock a Phantomdrive?

You write a file whose content contains the string password: followed by your password. The detection is content-based, so the file does not need to be named unlock.txt, and the drive unmounts itself, remounts the remaining disk and decrypts in place.

### What toolchain does Phantomdrive need?

The HydraUSB3 patched xPack GCC 12.2.0-1, which supports the WCH-Interrupt-fast interrupt attribute, installed into /usr/local from the released tarball. You also build the wch-ch56x-isp programming tool, and the firmware is a RISC-V target with the March flag set to rv32imac_zicsr.

### Can I change the AES mode or the PBKDF2 iteration count after flashing?

Not without losing access to the data. The AES mode and the KDF iteration count affect the on-disk format, so firmware built with different settings cannot decrypt the same data without migration or reformatting, and the documentation describes no migration path.

### Is Phantomdrive safe to use with real data?

The project states that the code has not been professionally audited, asks you to treat it as an experimental open source hardware and firmware project rather than a formally reviewed security product, and says the author is not responsible for loss of data or security incidents. The test README describes the current level of validation.

## Sources

- [Issues](https://github.com/o7-machinehum/phantomdrive/issues)
- [License: MIT](https://github.com/o7-machinehum/phantomdrive/blob/master/LICENSE)
- [o7-machinehum/phantomdrive on GitHub](https://github.com/o7-machinehum/phantomdrive)
- [README](https://github.com/o7-machinehum/phantomdrive/blob/master/README.md)
- [Releases](https://github.com/o7-machinehum/phantomdrive/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/o7-machinehum-phantomdrive
