Security FIELD NOTES · 4 MIN READ
Base32 Explained: The A-Z2-7 Alphabet Built for Humans
Five bytes in, eight characters out: the bit math, the missing digits, the padding table, and why TOTP secrets insist on Base32.
Base32 looks like a ransom note written in uppercase letters and a suspicious shortage of digits: JBSWY3DP, MZXW6YTBOI======. It is not encryption and not compression — it is a way of writing raw bytes using only 32 characters chosen to survive human eyes and systems that treat uppercase and lowercase as the same thing. Once you see the bit math, every quirk of the format stops looking arbitrary.
The bit math: 5 bytes in, 8 characters out
Bytes are 8 bits long, but Base32 packs symbols of 5 bits each. The least common multiple of 8 and 5 is 40, so the format's natural unit is 5 bytes of input becoming exactly 8 characters of output. Take the single byte 0x66 — the letter f:
byte 'f' = 0x66 = 0110 0110 (8 bits)
pad to a multiple of 5 = 0110 0110 00 (10 bits)
split into 5-bit groups: 01100 | 11000
01100 = 12 → 'M' 11000 = 24 → 'Y'
result: "MY" + "======" padding → MY======
The mapping from each 5-bit number to a character is fixed by RFC 4648: 0–25 map to A–Z and 26–31 map to 2–7. Encode the string Hello and you get its five bytes — 40 bits — dividing evenly into eight 5-bit groups: JBSWY3DP, no padding needed. The RFC's own test vector for foobar comes out as MZXW6YTBOI======, and the Base32 Encoder & Decoder reproduces all of these exactly.
Why the alphabet stops at 7
Thirty-two symbols is not a round number by accident: 25 = 32, one symbol per 5-bit group. But the choice of which 32 symbols is the clever part. Base32 uses only uppercase letters and the digits 2 through 7, deliberately excluding 0, 1, 8 and 9 — the characters that look like O, I/L and B when printed, handwritten, or read aloud over a bad phone line. Lowercase is excluded too, so a decoder can uppercase everything without ambiguity. The result is an alphabet you can transcribe from a router sticker without a single silent mistake.
Padding and the length table
Real input is rarely a multiple of 5 bytes; leftover bits are zero-filled rather than discarded. The trailing = characters carry no data — they pad the output so its length is always a multiple of 8, making malformed input easy to detect. The length table follows directly from the bit count:
input bytes output bits→chars example
1 2 chars + 6 '=' 'f' → MY======
2 4 chars + 4 '=' 'fo' → MZXQ====
3 5 chars + 3 '=' 'foo' → MZXW6===
4 7 chars + 1 '=' 'foob'→ MZXW6YQ=
5 8 chars + 0 '=' 'Hello' → JBSWY3DP
Decoding runs the film backwards: each character becomes its 5-bit value, the padding is ignored, and complete 8-bit groups are reassembled into bytes. The decoder here also strips whitespace and accepts lowercase, so wrapped or sloppily pasted Base32 still decodes cleanly.
Base32 vs Base64 vs hex
All three are binary-to-text encodings, and all three are interchangeable in the sense that they carry the same bytes. The trade-off is size versus robustness. Base64 packs 3 bytes into 4 characters (33% overhead) using a 64-symbol alphabet that includes lowercase and the easily-confused 0/O and 1/l. Base32 packs 5 bytes into 8 characters (60% overhead) in exchange for an unambiguous, case-insensitive alphabet. Hex is the extreme: 100% overhead but impossible to misread. If the data is going through machines, Base64 wins on size; if a human might have to read, type or say it, Base32 wins on survivability. For a wider tour of the whole family — including ASCII85 — see Beyond Base64: ASCII85 and Base32 Explained.
Where you actually meet Base32
The most common encounter is setting up an authenticator app: the "setup key" you type manually into the app is the account's secret encoded in Base32, precisely because a human is doing the transcription. The same alphabet shows up in DNSSEC records, license keys, recovery codes and QR-code payloads. The link to security here is structural rather than cryptographic: an authenticator's secret is fed into an HMAC-based time-based code scheme, which is its own topic — HMAC: How Messages Prove They Weren't Tampered With covers how that keyed hash works. Base32 itself provides zero secrecy: decoding is instant and keyless.
Try it yourself
Type Hello into the Base32 Encoder & Decoder and confirm you get JBSWY3DP — eight characters, no padding, because five bytes is the format's perfect case. Then type a single f to watch the padding appear as MY======, toggle padding off to see how decoders cope without it, and finally paste MZXW6YTBOI====== to decode the RFC's own test vector back into foobar.