base32.cr
base32.cr
Base32 encoding and decoding for Crystal, per RFC 4648: the standard alphabet of §6 (A-Z, 2-7) and the extended-hex alphabet of §7 (0-9, A-V).
Crystal's standard library ships Base64 but not Base32. This fills that gap with the same API shape, no dependencies, and no surprises: padding is optional, decoding is case insensitive, and malformed input raises instead of quietly producing the wrong bytes.
Installation
Add the dependency to your shard.yml:
dependencies:
base32:
github: aluminumio/base32.cr
version: ~> 0.1.0
Then run shards install.
Usage
require "base32"
Base32.encode("foobar") # => "MZXW6YTBOI======"
Base32.encode("foobar", padding: false) # => "MZXW6YTBOI"
Base32.encode(Bytes[1, 2, 3]) # => "AEBAG==="
Base32.decode("MZXW6YTBOI======") # => Bytes[102, 111, 111, 98, 97, 114]
Base32.decode_string("MZXW6YTBOI======") # => "foobar"
Base32.decode_string("mzxw6ytboi") # => "foobar"
The extended-hex alphabet of §7, which preserves the sort order of the underlying data, is available under the same names prefixed with hex_:
Base32.hex_encode("foobar") # => "CPNMUOJ1E8======"
Base32.hex_decode_string("CPNMUOJ1E8======") # => "foobar"
API
| Method | Returns |
|---|---|
Base32.encode(data : String | Bytes, padding : Bool = true) |
String |
Base32.decode(data : String | Bytes) |
Bytes |
Base32.decode_string(data : String | Bytes) |
String |
Base32.hex_encode(data : String | Bytes, padding : Bool = true) |
String |
Base32.hex_decode(data : String | Bytes) |
Bytes |
Base32.hex_decode_string(data : String | Bytes) |
String |
Encoding accepts a String (encoded as its UTF-8 bytes) or Bytes.
Decoding accepts either case, padded or unpadded input, and steps over CR and LF so that line-wrapped input round-trips. Everything else raises Base32::Error:
Base32.decode("MZXW-YTB") # Base32::Error: Invalid base32 character '-' (byte 0x2d)
Base32.decode("MZXW0YTB") # Base32::Error: Invalid base32 character '0' (byte 0x30)
Base32.decode("MZX") # Base32::Error: Invalid base32 length: 3 data characters
# cannot encode a whole number of bytes
Base32.decode("MZXW6===Y") # Base32::Error: Invalid base32 input: data character 'Y'
# follows the padding
Note that 0/O and 1/I/L are not treated as interchangeable. That substitution belongs to Crockford's Base32, which is a different encoding with a different alphabet; accepting it here would silently decode input that RFC 4648 says is invalid.
When to prefer Base32 over Base64
Base32 costs about 20% more characters than Base64 (8 characters per 5 bytes rather than 4 per 3). In exchange:
- It is case insensitive. The alphabet has a single case, so decoding accepts either. Anything that may case-fold a value in transit will corrupt a mixed-case Base64 string but leave a Base32 one intact — DNS labels, email local-parts rewritten by intermediate mail servers, case-insensitive filesystems, values a human may retype or a system may normalise.
- Its alphabet contains no delimiters. No
+,/,-or_, so an encoded value can be embedded in a URL, a filename, or a structured address without escaping and without colliding with the separators around it. Withpadding: falsethe output is[A-Z2-7]+and nothing else.
Reach for Base64 whenever size matters and the transport is byte-transparent and case-preserving. Reach for Base32 when the value has to survive a channel that is neither — a signed token in an email address, a lookup key in a hostname, an identifier a person will read aloud.
Development
$ crystal spec
$ crystal tool format --check
The specs cover the RFC 4648 §10 test vectors for both alphabets, round trips over every length up to 40 bytes, unpadded output, case-insensitive decoding, and each rejection path. Output is also verified byte-for-byte against CPython's base64.b32encode / b32hexencode.
Contributing
- Fork it (https://github.com/aluminumio/base32.cr/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Licence
MIT. See LICENSE.
base32.cr
- 0
- 0
- 0
- 0
- 0
- about 5 hours ago
- August 15, 2026
MIT License
Sat, 15 Aug 2026 20:44:44 GMT