logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·31.10.26
← Back to news

Base84 deserves a place in file names

Original article
00f.net|38 points|28 comments|by kevvok|Sep 11, 2026

The Case for Base84 in Filename Encoding

Originally published by Frank DENIS on September 9, 2026

The development of TurboCrypt, a tool designed for file and git encryption, began with a focus on Unix-like environments. To handle encrypted filenames, the developers initially implemented Base91.

Base91 was considered a perfect fit because it generates strings that are valid across Unix and macOS filesystems. However, this perspective ignored the complexities of various libraries and the eventual need for cross-platform compatibility.

The Windows Compatibility Hurdle

When the demand for Windows support emerged, the team hit a wall. Windows forbids several characters that are perfectly acceptable in the Unix-safe alphabet used by Base91. While some might suggest adding a dot to the alphabet, a filename ending in a . is unreliable when interacting with standard Windows file APIs or the shell.

By removing the dot entirely, we arrive at a set of 84 characters that are universally safe for filename components across Linux, macOS, and Windows. Furthermore, excluding dots prevents the accidental creation of "hidden" files on Unix systems (those starting with .) or the special . and .. directory references.

The Base84 Alphabet

The characters are ordered as follows for encoding:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$% '()+,-;=@[]^_`{}~

Technical Efficiency and Mathematics

Base84 is designed for machine-to-machine communication; human readability is not a priority. The goal is maximum filesystem compatibility with minimal data expansion compared to Base64.

The "sweet spot" for packing is five characters. Using LaTeX to express the capacity: 845=4,182,119,42484^5 = 4,182,119,424845=4,182,119,424

This value is only 2.6%2.6\%2.6% shy of 2322^{32}232. Consequently, a group of five characters can hold 32 bits of data approximately 95%95\%95% of the time (given uniformly random input), and 31 bits otherwise. Specifically, if the value is below 845−23184^5 - 2^{31}845−231, a 32nd bit can be stored.

Efficiency Metrics:

  • Average bits per character: ≈6.39\approx 6.39≈6.39
  • Average bits per group: ≈31.95\approx 31.95≈31.95
Encoding SchemeAverage ExpansionWorst-case Expansion
Base6433.3%33.3\%33.3%33.3%33.3\%33.3%
Base8425.2%25.2\%25.2%29.0%29.0\%29.0%

Note: The worst-case scenario occurs with an input of 0xff, forcing each full group to consume only 31 bits. Because 5 divides 255 exactly, a maximum-length name consists of complete groups, ensuring no bits are lost. Base84 can accommodate 197197197 bytes of input, whereas unpadded Base64 is limited to 191191191.

Comparison with Other Systems

The requirements for filenames vary wildly by OS:

  • Unix/Linux: Almost any character is allowed except for the null byte (NUL) and the forward slash (/).
  • Windows: Much more restrictive.

Some implementations, like the filesystem variant of zig-base91, attempt to fix Base91 by replacing the slash (/) with an apostrophe ('). This achieves ≈6.51\approx 6.51≈6.51 bits per character (roughly 23%23\%23% expansion), but it still contains characters that Windows rejects.

Avoiding Windows Reserved Names

Windows reserves specific device names (e.g., CON, NUL, COM1, LPT1) regardless of case. Base84 inherently avoids these through its structure:

  • Length Constraints: A four-character output always ends in an uppercase letter or a, b, or c.
  • Digit Exclusion: It cannot end in a digit, making COM1-COM9 impossible.
  • Character Set: Superscript digits and dots are excluded.
  • No Padding: Unlike Base64, no special padding is required to avoid reserved names.

Handling Case-Insensitivity Collisions

Since Base84 distinguishes between A and a, but some filesystems do not, there is a theoretical risk of collisions. TurboCrypt mitigates this by padding short names to 16 bytes and applying HCTR2.

If we treat the encryption as a random permutation, we get 128-bit ciphertexts. Even if every character were a letter, there are only 2212^{21}221 possible case variations.

The probability of a case collision between two distinct encrypted names is: 2212128=2−107\frac{2^{21}}{2^{128}} = 2^{-107}2128221​=2−107

For a directory containing nnn distinct names, the birthday bound for a collision is: n(n−1)2108\frac{n(n - 1)}{2^{108}}2108n(n−1)​


Related Reading:

  • Sep 09, 2026: Semi-public Git repositories
  • Aug 19, 2026: Why compiling Rust to WebAssembly is slow
  • Jul 30, 2026: An improved attack on 7-round AES
  • Jul 16, 2026: AES gets swizzled
  • Jul 08, 2026: The best WebAssembly runtime may still be no runtime at all