According to this post, S-boxes are invertible.
Inverting S-boxes can be very easy: you simply create a lookup table that reverse all the possible substitutions of the S-box. E.g. if the S-box maps 0xA5 to 0x3F (this would be an 8x8 S-box), then the inverse transformation would map 0x3F to 0xA5. Thus, you simply enumerate all the possible values the S-box can have, and create an inverse table that "undoes" all those transformations (this effectively limits how large the S-boxes can be in practice).
Now, consider f
, which XOR the higher bits in x
by a mask determined by the lower bits with an S-box.
s_box = [
8, 7, 0, 10, 1, 3, 5, 12,
11, 13, 15, 14, 2, 6, 9, 4
]
def f(x):
x ^= s_box[x & 0xF] << 2
return x
It turns out that this particular configuration is not invertible, since it produces fewer than 256 unique outputs.
>>> unique = set(f(x) for x in range(0xFF))
>>> len(unique)
192
Question: how do I design the S-box and/or shift amount such that shifted XOR with an S-box is invertible? Bonus points if your answer applies to other reversible integer operations, e.g. multiplication by an odd integer determined by an S-box.
In case you are wondering about the background, I am designing a perfect (i.e. collision-free) hash function that maps a 14-bit input to a 14-bit output. It doesn't need to be cryptographically secure, but I want good statistical properties (e.g. the avalanche effect). Intuitively, S-boxes can induce good mixing, but I cannot afford to store an S-box of $2^{14}$ entries, so I'm considering reversible integer operations based on a tiny S-box lookup. Moreover, I want to minimize the computational cost of $f$, but I don't care how long it takes to compute $f^{-1}$. Any suggestions are appreciated.
x & 0xF
extract the 4 LSB bits, sos_box[x & 0xF]
is not invertible, but my question is how to make $f$ invertible, i.e. computex
fromx ^ (s_box[x & 0xF] << 2)
. $\endgroup$hash ^= hash << 3
are invertible despite using<<
. See for example this page. $\endgroup$