I updated a bunch of fields in my db using this Feistel Cipher. According to the documentation, the cipher can be undone to get the original value. How can I undo the values if need be?
Here is the original cipher function:
CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE int) returns bigint AS $$
DECLARE
l1 int;
l2 int;
r1 int;
r2 int;
i int:=0;
BEGIN
l1:= (VALUE >> 16) & 65535;
r1:= VALUE & 65535;
WHILE i < 3 LOOP
l2 := r1;
r2 := l1 # ((((1366.0 * r1 + 150889) % 714025) / 714025.0) * 32767)::int;
l1 := l2;
r1 := r2;
i := i + 1;
END LOOP;
RETURN ((l1::bigint << 16) + r1);
END;
$$ LANGUAGE plpgsql strict immutable;
You could use this self-reversible variant in the first place:
CREATE FUNCTION rev_pseudo_encrypt(VALUE bigint) returns bigint AS $$
DECLARE
l1 int;
l2 int;
r1 int;
r2 int;
i int:=0;
BEGIN
l1:= (VALUE >> 16) & 65535;
r1:= VALUE & 65535;
WHILE i < 3 LOOP
l2 := r1;
r2 := l1 # ((((1366.0 * r1 + 150889) % 714025) / 714025.0) * 32767)::int;
l1 := l2;
r1 := r2;
i := i + 1;
END LOOP;
RETURN ((r1::bigint<<16) + l1);
END;
$$ LANGUAGE plpgsql strict immutable;
It differs from the original version by taking bigint
instead of int
as input (but the input should still less than 2^32
), and by the fact that the two 16 bits blocks are swapped in the 32 bits final result.
It has the property that rev_pseudo_encrypt(rev_pseudo_encrypt(x)) = x
in addition to the uniquess of results.
Also there's the advantage that the input type is the same as the output type.
On the other hand, to reverse the values generated by the original version, their 16 bits blocks need to be swapped before being fed to the algorithm, and the result again swapped:
create function swap16(bigint) returns bigint as
'select (($1&65535)<<16)+(($1)>>16)'
language sql stable;
select pseudo_encrypt(1234);
pseudo_encrypt
----------------
223549288
select swap16(pseudo_encrypt(swap16(223549288)::int));
swap16
--------
1234