Winix.Unprotect
0.4.0
Prefix Reserved
dotnet tool install --global Winix.Unprotect --version 0.4.0
dotnet new tool-manifest
dotnet tool install --local Winix.Unprotect --version 0.4.0
#tool dotnet:?package=Winix.Unprotect&version=0.4.0
nuke :add-package Winix.Unprotect --version 0.4.0
unprotect
Companion to protect — decrypts files that protect encrypted using the same native OS key-storage primitives (DPAPI on Windows, Keychain on macOS, libsecret on Linux). Single AOT native binary. Zero key management.
Why
Files encrypted with protect are scoped to a specific user and machine. unprotect reverses the encryption using the same OS key that encrypted them. If the key is gone (different user, different machine, or cleared key store), decryption fails — a deliberate safety feature.
Install
Scoop (Windows)
scoop bucket add winix https://github.com/Yortw/winix
scoop install unprotect
.NET global tool (any platform with .NET 10+)
dotnet tool install --global Winix.Unprotect
Native binary (GitHub Releases)
Download the platform-appropriate archive from releases and place unprotect on your PATH.
Usage
unprotect file.json.prot # decrypts to file.json
unprotect file.json.prot -o plaintext.json # explicit output path
unprotect file.json.prot --in-place # decrypt over the file (atomic)
unprotect < encrypted.prot > plaintext.bin # streaming (pure piping)
unprotect file.prot --rm # delete encrypted file after success
Options
| Flag | Default | Description |
|---|---|---|
-o PATH / --output PATH |
FILE (strips .prot), or stdout for stdin |
Explicit output path. |
--in-place |
off | Decrypt/decrypt over the input file (atomic via temp + rename). |
--rm / --remove-source |
off | Delete source after successful decryption. |
--scope {user,machine} |
user |
Key-derivation scope (must match the scope used to encrypt). |
--no-verify |
off | Skip round-trip verification (faster, less safe). |
-f / --force |
off | Overwrite an existing destination file. By default the tool refuses to clobber existing data and exits 125. The overwrite is symlink-safe — if the destination is a symlink, the symlink itself is removed (the target file is untouched) before an exclusive create. |
--color, --no-color |
— | Respect NO_COLOR. |
--describe |
— | Emit tool metadata as JSON. |
--help, --version |
— | Standard introspection. |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 125 | Usage error — bad flags, path collision, scope mismatch. |
| 126 | Runtime error — decryption failure, wrong platform/user, key store unavailable, round-trip verify failed. |
How It Works
unprotect reads the encrypted file's header to determine:
- Which platform originally encrypted it (Windows DPAPI, macOS Keychain, or Linux libsecret)
- Which scope was used (user or machine)
- Whether it's still encrypted (vs already plaintext)
It then decrypts using the same OS key that was used to encrypt. If the key is no longer available (different user, different machine, or key deleted), decryption fails with a clear error message.
Files are chunked (64 KB per chunk) and each chunk is encrypted with AEAD (AES-256-GCM) or DPAPI. Every chunk is bound to its specific file via a 16-byte random FileId stored in the header — chunk reorder, cross-file substitution, and truncation are all detected via AEAD authentication tags (GCM) or DPAPI integrity checks.
Integrity Model
Each chunk is authenticated with both its position (chunk index, isFinal flag) and the file's per-file FileId. This prevents:
- Truncation — silent removal of chunks (final-chunk flag).
- Reorder — swapping chunks within a file (chunk index in AAD).
- Cross-file substitution — splicing a chunk from a different
.protfile into this one (FileId in AAD).
Threat model boundary. The protections above defend against an attacker with write access to your .prot file but no key access — for example, untrusted backup software, cloud sync services, or shared NAS storage. If the attacker has both filesystem write AND access to your OS keystore (same OS user, or LocalMachine scope on a shared host), they can decrypt directly — splice protections are not the relevant defense.
Platform Notes
- Windows: Both user and machine scopes are supported. Machine-scope files require the process to have DPAPI LocalMachine access.
- macOS: User scope uses the login Keychain. Machine scope uses the System Keychain and requires admin privileges.
- Linux: Only user scope is supported. The tool requires
libsecret-toolsto be installed.
Cross-Platform Failures (by Design)
unprotect will fail clearly if:
- The file was encrypted on a different machine or by a different user (wrong key)
- The file was encrypted with machine scope but you're running user scope (or vice versa)
- The OS key store has been cleared or the key deleted
- The file has been truncated or tampered with
Composition
# Decrypt an HMAC key and use it
unprotect < api.key.prot | digest --hmac sha256 --key-stdin "payload"
# Decrypt a config, pass to an application
unprotect config.prot | myapp --config -
# Pipe through a decompressor
unprotect < backup.prot | zcat | tar -x
Related Tools
protect— companion; encrypts filesdigest— hash files (pairs with encrypted keys)clip— clipboard bridge for credentials
See Also
man unprotect(afterwinix install man)unprotect --describefor JSON metadata
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
## [0.3.0] - 2026-05-10
- Initial release.
See full changelog at https://github.com/Yortw/winix/blob/main/src/unprotect/CHANGELOG.md