# base64 for Windows Windows has no `base64` command. `certutil -encode` is the usual substitute, but it only works on files, wraps output in `-----BEGIN CERTIFICATE-----` markers, and can't be piped. This is a real `base64.exe` that behaves like the one on a Linux box, so the commands and scripts you already know carry over. ```powershell base64 image.png > image.b64 base64 -d image.b64 > image.png type secrets.json | base64 -w0 base64 -di messy.txt > payload.bin ``` ## Build and install ```powershell cargo build --release ``` Copy `target\release\base64.exe` somewhere on your PATH — e.g. `%LOCALAPPDATA%\Microsoft\WindowsApps`, or a `C:\tools\bin` folder you've added yourself: ```powershell mkdir C:\tools\bin copy target\release\base64.exe C:\tools\bin\ [Environment]::SetEnvironmentVariable( 'Path', [Environment]::GetEnvironmentVariable('Path','User') + ';C:\tools\bin', 'User') ``` Open a new terminal afterwards for the PATH change to take effect. No runtime dependencies — it's a single static binary, and the crate itself has zero dependencies, so the build works offline. ## Usage ``` Usage: base64 [OPTION]... [FILE] Base64 encode or decode FILE, or standard input, to standard output. With no FILE, or when FILE is -, read standard input. -d, --decode decode data -i, --ignore-garbage when decoding, ignore non-alphabet characters -w, --wrap=COLS wrap encoded lines after COLS character (default 76). Use 0 to disable line wrapping --help display this help and exit --version output version information and exit ``` Long options accept unambiguous abbreviations (`--dec`), short options cluster (`-di`), `-w` takes its value joined or separate (`-w0`, `-w 0`, `--wrap=0`), and `--` ends option parsing. ## Behaviour Verified against GNU coreutils 9.7 with a 153-case differential test covering encoding at every wrap width, decode round-trips, malformed padding, truncated input, garbage handling, all the usage errors and file operands. Output, exit codes and error messages match, including the fiddly parts: - **Partial output on failure.** Bytes are emitted as soon as enough bits are available, so a stream that goes bad halfway still writes the valid prefix before the error — same as coreutils. - **Unpadded input is accepted** when the encoding is canonical, i.e. the leftover bits are zero. `aGVsbG8` decodes to `hello`; `aGV` is rejected because `V` carries stray bits. - **Concatenated streams** decode as one (`aGVsbG8=aGVsbG8=` → `hellohello`). - **`-w0` emits no trailing newline**; wrapped output always ends with one. Empty input produces no output at all. - **Exit codes**: `0` success, `1` on any error. ### Deliberate differences | | Why | |---|---| | `\r` is skipped when decoding, as `\n` already is | GNU rejects CRLF input as invalid. On Windows a base64 file will routinely have CRLF line endings, and erroring on those would make the tool useless for its main job. Everything else is still rejected unless you pass `-i`. | | `--help` omits GNU's support URLs | This isn't GNU coreutils; pointing at their bug tracker would be wrong. | | Error messages quote with `'ASCII'` | GNU uses `'…'` in a UTF-8 locale. Windows consoles are frequently cp437/cp1252, where those bytes render as mojibake. | ## Notes - Roughly 2× slower than GNU on large inputs (100 MB encodes in ~0.35s vs ~0.18s). GNU uses hand-tuned routines; this is plain portable Rust and fast enough that the difference is invisible for normal use. - Input is streamed, not buffered whole, so encoding a multi-gigabyte file doesn't blow up memory. - Piping to a program that exits early (`base64 big.bin | head`) is handled quietly rather than reporting a broken pipe. - Writing raw binary to a Windows console fails with a clear message telling you to redirect, because consoles take UTF-16 text and would corrupt the bytes. Redirecting to a file or piping is unaffected.