I need a way to map some printable text to other printable text. E.g.:
Ian Boyd
→ Kcp Zbas
Notice some of the important requirements:
- uppercase is uppercase in output
- lowercase in input is lowercase in input
- spaces (and anything else outside of A-Z0-9) are left alone
The additional requirement is that it be deterministic, that is the same input always gives the same output:
Ian Boyd
→Kcp Xbas
Ian Boyd
→Kcp Xbas
Ian Boyd
→Kcp Xbas
The other requirement expands on determinism, and i don't know what to call it except to say that words with common prefixes needs to have the same output for the same common prefix:
I
→K
Ia
→Kc
Ian
→Kcp
Ian
→Kcp
Ian B
→Kcp X
Ian Bo
→Kcp Xb
Ian Boy
→Kcp Xba
Ian Boyd
→Kcp Xbap
My Solution
I created a solution to these technical requirements 15 years ago; but i'm trying to revisit it some something "better".
My solution was a "a streaming Caesar cipher with chaining".
Create a simple caesar substitution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
F H U D I R Y E K C Z N S A B G J P V O W T L M Q X
But rather than a simple substitution:
Ian Boyd
→Fks Hbqd
instead i used chaining:
Previous State Current Character Sum (mod) Next character output
-------------- ----------------- ---------- -------------- --------------------
0 I (9) 9 K K
9 a (1) 10 c Kc
10 n (14) 24 p Kcp
24 B (2) 26 X Kcp X
26 o (15) 15 b Kcp Xb
15 y (25) 14 a Kcp Xba
14 d (4) 18 m Kcp Xbap
Better solution with hashing?
These values are not something i need (or want) to "decrypt", and the use of a caesar cipher implies the ability to decrypt (which we all know isn't that hard).
So conceptually i really want a "one-way function": something that:
- converts input
- to some unpredictable output
- deterministically
I thought: what if i used a hash algorithm, adding letters on by one, and getting the current "state", and convert that partial digest to a character (uppercase/lowercase/digit as appropriate to match the input):
String FrobTheGrobber(String input)
{
//proof of concept pseudocode that only handles uppercase
HashAlgorithm hash = new SHA256();
hash.AddBytes(SECRET_KEY);
String res = "";
for Char ch in input
{
hash.Add(ch);
int charCode = (hash.Hash[0] % 26) + 1; //use first byte, mod 26 to get a value from 0..25
res += Char(Ord('A') + charCode;
}
}
I know; you hate the requirements.
Can anyone thing of anything better?
I
→K
, theni
→k
, or could it be thati
→z
? That ifIan → Kcp
thenIan Ian
→Kcp Kcp
, or could it be thatIan Ian
→Kcp Zar
? $\endgroup$i
andI
both map tok
andK
. The full solution also includes0
-9
in the Caesar cipher. This way things like phone numbers map to something that also looks like a phone number (905-867-5309
→619-112-8408
). And i could also map lowercase separately from uppercase. Either way: doesn't matter. If someone can come up with a solution forA
-Z
, i can expand it to any other characters. $\endgroup$