Hexadecimal strings typically contain 0–9 and a–f, which gives plenty of opportunity for (naughty) words to form (Batchelder, 2007), e.g. “deadbabe”.
If we also accept “9” as a substitute for “g,” then even more (naughty) words can be built, e.g. “defacedfa9”. Your imagination is the limit.
To avoid accidentally generating words, we can remove vowels (A E I O U, and arguably Y) from the list of possible characters, and additionally remove characters that can stand in for vowels (0 1 3 4).
The set of characters we use for representing hexadecimal strings therefore becomes shifted: instead of 0123456789abcdef
, we now have 256789bcdfghjklm
.
Performing this replacement is trivial with tr
on the command line:
% echo 1337cafebabe | tr 0123456789abcdef 256789bcdfghjklm
577cjgmlhghl
Ruby’s String#tr
does the same:
'1337cafebabe'.tr('0123456789abcdef', '256789bcdfghjklm')
# => "577cjgmlhghl"
We can go further and remove ambiguous characters as well. While not necessary, it is helpful when writing down hexadecimal strings (especially when using non-conventional characters, like we do).
Ambiguous characters include B D G J Q S Z and 0 1 2 5 6 8 (with 0 and 1 already removed because they can substitute a vowel). The set of characters we use for representing hexadecimal strings has shifted again: instead of 256789bcdfghjklm
(which replaced 0123456789abcdef
), we now have 79cfhklmnprtvwxz
.
We can use it the same way. Command line:
% echo 1337cafebabe | tr 0123456789abcdef 79cfhklmnprtvwxz
9ffmvrzxtrtx
Ruby:
'1337cafebabe'.tr('0123456789abcdef', '79cfhklmnprtvwxz')
# => "9ffmvrzxtrtx"
Note how 79cfhklmnprtvwxz
is exactly 16 characters and ends with z
. A wonderful coincidence.
Good human-usable tokens are not random strings
Batchelder, Ned. “Hex Words,” March 20, 2007. https://nedbatchelder.com/text/hexwords.html.