Converting an int to a pseudo-random string and vice versa

505 Views Asked by At

I need to create two C# functions, one (called f1) to convert an integer to a string and another (called f2) to convert that string to the starting integer. The strings should look like a random set of 5-character (letters and numbers). It must be ensured that there are no collisions (two possible integers for the same string) for at least the first 16 million integers (I'm not interested in converting too large numbers).

//Where the "BytesToBase32" function converts a byte array to base 32 ()
string IntToString(int id) {
    byte[] bytes = BitConverter.GetBytes(id);
    return Utils.BytesToBase32(new byte[] {
        (byte)(004 + 145 * bytes[0] + 113 * bytes[1] + 051 * bytes[2]),
        (byte)(166 + 237 * bytes[0] + 010 * bytes[1] + 212 * bytes[2]),
        (byte)(122 + 171 * bytes[0] + 135 * bytes[1] + 020 * bytes[2])
    });
}

It returns values like this:

  • 0 --> 0ij7k
  • 1 --> im9ia
  • 2 --> 4q0d0
  • 3 --> mtmnm
  • ...

As you can see the strings seem random (in other words it is not possible to understand that "0ij7k" comes before "im9ia" and vice versa).

The problem is that the function f2 cannot be obtained by simply solving the 3 equation system used by f1. Is there a simpler way to get f1 and f2?

2

There are 2 best solutions below

0
Dialecticus On BEST ANSWER

In the comments I was wrong about the stream cipher. You actually need a block cipher, but the size of your block must be 24 bits (for 16 million integers). See this question and the answer: https://crypto.stackexchange.com/q/18988

These types of ciphers are called Format-preserving encryption (FPE). One such FPE is called FF1.

There is a C# implementation of FF1 on github: https://github.com/a-tze/FPE.Net

1
Jamiec On

It may be good enough to use a cipher algorithm like RC4 (code sample taken from here) to encrypt the bytes of your int and then simply Base64 encode those bytes to generate the string. This will always be the same length for any int.

var pwd = Encoding.UTF8.GetBytes("SomePassword");
var enc = Convert.ToBase64String(RC4.Encrypt(pwd, BitConverter.GetBytes(input)));
var dec = BitConverter.ToInt32(RC4.Decrypt(pwd, Convert.FromBase64String(enc)));

Live example: https://dotnetfiddle.net/gK593y

This sample does suffer from your issue of "adjacent numbers"

16: SB8u1Q==
17: SR8u1Q==
18: Sh8u1Q==
19: Sx8u1Q==

I'm not sure how much of an issue this is for you.