Virsimo.CryptFS 0.1.0

dotnet add package Virsimo.CryptFS --version 0.1.0
                    
NuGet\Install-Package Virsimo.CryptFS -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Virsimo.CryptFS" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Virsimo.CryptFS" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Virsimo.CryptFS" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Virsimo.CryptFS --version 0.1.0
                    
#r "nuget: Virsimo.CryptFS, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Virsimo.CryptFS@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Virsimo.CryptFS&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Virsimo.CryptFS&version=0.1.0
                    
Install as a Cake Tool

CryptFsNet

A C# port of gocryptfs: a library that reads and writes gocryptfs encrypted directories, and a cryptfs command line tool built on it.

The on-disk format is the real thing, not a look-alike. Filesystems created by gocryptfs open here, and filesystems created here open in gocryptfs. This is verified by running the port against the example filesystems that ship with gocryptfs, covering every format generation from v0.7 to v2.2 and all three content-encryption backends.

  • src/CryptFsNet — the library (net10.0, no external dependencies).
  • src/CryptFsNet.Cli — the cryptfs command line tool.
  • tests/CryptFsNet.Tests — 90 tests: RFC test vectors, gocryptfs interoperability, round trips, CLI.
  • gocryptfs/ — a checkout of upstream gocryptfs, used as the reference and as the source of test fixtures.

What is ported

Area Status
AES-256-GCM content encryption (default) ✅
AES-SIV-512 content encryption (-aessiv) ✅
XChaCha20-Poly1305 content encryption (-xchacha) ✅
EME filename encryption, per-directory IVs ✅
Long names (gocryptfs.longname.*) ✅
PlaintextNames, DeterministicNames, Raw64, LongNameMax, HKDF feature flags ✅
Pre-v1.3 filesystems (no HKDF, 96-bit key IV, padded base64) ✅
scrypt master key wrapping, gocryptfs.conf read and write ✅
Symlinks (encrypted targets) ✅
Random-access read, write, truncate, sparse files ✅
Password change, master key export and import ✅
FUSE mounting ❌ — no kernel filesystem; use extract/import
Reverse mode (-reverse) ❌ — the PathIv derivation is ported, the frontend is not
Extended attributes ➖ — name encryption is ported, no xattr I/O
FIDO2-protected master keys ➖ — detected and reported; open such a filesystem with --masterkey
Control socket (-ctlsock) ❌ — the encpath/decpath commands cover the same need

Mounting is the one substantial gap. gocryptfs is a FUSE daemon; there is no portable managed equivalent, and a Windows implementation would need a third-party driver (WinFsp or Dokan). The library is structured so that a mount layer could sit on top of CryptFs and CryptFileStream without changing anything below them.

Quick start

$ dotnet build -c Release
$ alias cryptfs='./src/CryptFsNet.Cli/bin/Release/net10.0/cryptfs'

$ cryptfs init myvault              # create an encrypted directory
$ cryptfs import myvault ~/Documents # encrypt a tree into it
$ cryptfs ls -l myvault
$ cryptfs cat myvault notes/todo.md
$ cryptfs extract myvault ./restored # decrypt it all back out
$ cryptfs fsck myvault               # verify every block authenticates

cryptfs help lists all commands: init, info, passwd, dumpmasterkey, ls, tree, cat, get, readlink, put, mkdir, rm, mv, ln, extract, import, encpath, decpath, fsck, xray, speed.

Passwords come from -p PASS, --passfile FILE, --extpass CMD, the CRYPTFS_PASSWORD environment variable, or an interactive prompt. --masterkey HEX bypasses the password entirely. Exit codes match gocryptfs (12 for a wrong password, 26 for fsck errors, and so on).

Library usage

using CryptFsNet.Fs;

// Create a filesystem
using (var fs = CryptFs.Create("myvault", new CryptFsInitOptions { Password = "hunter2" }))
{
    fs.CreateDirectory("notes");
    fs.WriteAllText("notes/todo.md", "- port gocryptfs\n");
}

// Open one gocryptfs made
using (var fs = CryptFs.Open("myvault", "hunter2"))
{
    foreach (CryptFsEntry entry in fs.ListDirectory("notes"))
        Console.WriteLine($"{entry.Name} {entry.Length}");

    // Streams are seekable and support partial writes and truncation
    using CryptFileStream stream = fs.OpenReadWrite("notes/todo.md");
    stream.Seek(0, SeekOrigin.End);
    stream.Write("- write a README\n"u8);
}

CryptFs works in plaintext paths (/ separated, relative to the vault root) and handles the encrypted names, directory IV files and long-name sidecars underneath. EncryptPath and DecryptPath expose the translation directly if you need it.

The lower layers are public too, so you can use them without the filesystem wrapper: ConfigFile (gocryptfs.conf), CryptoCore (key derivation), ContentEnc (block encryption), NameTransform (EME names), and the primitives in CryptFsNet.Crypto.

How it maps to gocryptfs

gocryptfs CryptFsNet
internal/cryptocore CryptoCore, Crypto/
internal/contentenc ContentEnc, FileHeader
internal/configfile Config/ConfigFile, Config/ScryptKdf, Config/FeatureFlag
internal/nametransform Names/NameTransform, Names/DirIv, Names/LongNames, Names/Base64Url
internal/siv_aead Crypto/AesSivCipher
internal/stupidgcm Crypto/AesGcmCipher, Crypto/XChaCha20Poly1305Cipher
internal/pathiv PathIv
internal/fusefrontend Fs/CryptFs, Fs/CryptFileStream
gocryptfs-xray cryptfs xray, cryptfs dumpmasterkey

Cryptography that had to be written from scratch

The .NET base class library does not cover everything gocryptfs needs, so these are implemented in src/CryptFsNet/Crypto and pinned by test vectors:

  • AES-GCM with 128-bit nonces. The BCL's AesGcm only accepts 96-bit nonces, and there is no way to supply a pre-counter block of your own. GCM is therefore built on hardware-accelerated AES-ECB plus a table-driven GHASH. It is checked against the BCL where the two overlap (96-bit nonces) and against gocryptfs's own ciphertext for the 128-bit case.
  • AES-SIV (RFC 5297), including AES-CMAC and S2V. Verified against RFC 5297 appendix A.1 and A.2.
  • XChaCha20-Poly1305, including HChaCha20. System.Security.Cryptography.ChaCha20Poly1305 is not available on every platform and does not expose HChaCha20.
  • scrypt (RFC 7914), on top of the BCL's PBKDF2. Verified against three RFC 7914 vectors.
  • EME, the wide-block mode used for file names, ported from github.com/rfjakob/eme.

Verification

$ dotnet test
Passed!  - Failed: 0, Passed: 90, Skipped: 0, Total: 90

The interoperability tests read the seven example filesystems in gocryptfs/tests/example_filesystems and the fixtures in gocryptfs/gocryptfs-xray/xray_tests, which were all produced by the real gocryptfs. They check that:

  • the password unlocks the master key (scrypt plus the AES-GCM key wrapping),
  • file content decrypts and authenticates on all three backends,
  • file names, directory IVs and long names decrypt,
  • re-encrypting a decrypted name reproduces gocryptfs's bytes exactly — EME is deterministic, so this pins the encrypt direction, not just decryption,
  • cryptfs xray output is byte-identical to the recorded gocryptfs-xray output,
  • dumpmasterkey returns the master key documented in gocryptfs's own test.

Note on the vendored checkout: the fixtures are binary files, so the gocryptfs checkout must not have gone through CRLF conversion. core.autocrlf is set to false and core.longpaths to true in gocryptfs/.git/config for this reason. Without that, v1.3/gocryptfs.diriv comes out 17 bytes instead of 16 and that filesystem fails to open.

Performance

cryptfs speed on a release build, 4 KiB blocks:

AES-GCM-256                  337 MiB/s
AES-SIV-512                  333 MiB/s
XChaCha20-Poly1305            75 MiB/s

Fast enough to be disk-bound for most work, but well short of gocryptfs with OpenSSL. The limit is GHASH for the AES modes and the managed ChaCha20 core for XChaCha20. A PCLMULQDQ GHASH would be the next significant win; it is deliberately not implemented, because the table version is portable and its correctness is pinned by the test vectors.

Security notes

  • This is a port, not an audit. The format and algorithms follow gocryptfs, but the implementation is new code and has not been reviewed by anyone else.
  • Managed memory cannot be reliably wiped: the garbage collector may copy a key before CryptographicOperations.ZeroMemory runs. Keys are zeroed where practical, which raises the bar without being a guarantee.
  • The table-driven GHASH and the AES-ECB keystream are not hardened against cache-timing attacks on the same machine. Tag comparison is constant time.
  • -p PASS puts the password in the process list. Prefer --passfile, --extpass, or the prompt.

License

gocryptfs itself is MIT licensed; see gocryptfs/LICENSE. This port, however, is proprietary — see LICENSE.txt. No permission is granted to use, copy, modify, or distribute this software without the copyright holder's prior written permission.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 138 9/3/2026