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:
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?