Skip to content

Instantly share code, notes, and snippets.

@JasonKleban
Last active December 23, 2025 16:35
Show Gist options
  • Select an option

  • Save JasonKleban/b73324a919400e15bf335a4efa75599e to your computer and use it in GitHub Desktop.

Select an option

Save JasonKleban/b73324a919400e15bf335a4efa75599e to your computer and use it in GitHub Desktop.
Postgres implementation of a 63-bit (unbalanced) feistel cipher
-- AZURE doesn't support this extension yet
-- ;CREATE EXTENSION IF NOT EXISTS permuteseq;
CREATE OR REPLACE FUNCTION feistel_cipher_63(
value bigint,
crypt_key bigint,
decrypt boolean DEFAULT false
)
RETURNS bigint LANGUAGE plpgsql STRICT IMMUTABLE PARALLEL SAFE
AS $$
DECLARE
num_rounds constant int := 12;
result bigint;
L31 bigint; R32 bigint;
Rhi bigint; R31 bigint;
s int; i int; Fi bigint;
kh bigint; kl bigint; Ks bigint;
BEGIN
IF value < 0 THEN RAISE EXCEPTION 'Value must be non-negative'; END IF;
-- Initial split
L31 := (value >> 32) & x'7FFFFFFF'::bigint;
R32 := value & x'FFFFFFFF'::bigint;
kh := (crypt_key >> 32) & x'FFFFFFFF'::bigint;
kl := crypt_key & x'FFFFFFFF'::bigint;
FOR i IN 0..num_rounds - 1 LOOP
s := CASE WHEN decrypt THEN num_rounds - 1 - i ELSE i END;
Ks := (CASE WHEN (s & 1) = 0 THEN kl ELSE kh END) * s;
IF NOT decrypt THEN
-- ENCRYPT
Rhi := (R32 >> 31) & 1;
R31 := R32 & x'7FFFFFFF'::bigint;
Fi := (R31 # Ks);
-- Stir ...
Fi := (Fi # (Fi << 13)) & x'FFFFFFFF'::bigint;
Fi := Fi # (Fi >> 17);
Fi := (Fi # (Fi << 5)) & x'7FFFFFFF'::bigint;
R32 := ((L31 # Fi) << 1) | Rhi;
L31 := R31;
ELSE
-- DECRYPT (explicit inverse)
Rhi := R32 & 1;
R31 := L31;
Fi := (R31 # Ks);
-- Stir ...
Fi := (Fi # (Fi << 13)) & x'FFFFFFFF'::bigint;
Fi := Fi # (Fi >> 17);
Fi := (Fi # (Fi << 5)) & x'7FFFFFFF'::bigint;
L31 := (R32 >> 1) # Fi;
R32 := (Rhi << 31) | R31;
END IF;
END LOOP;
RETURN (L31 << 32) | R32;
END;
$$;
/*
;WITH const AS (SELECT 6287830383155378919::bigint as key)
SELECT
values.seq as original
,LPAD(to_hex(feistel_cipher_63(values.seq, const.key)), 16, '0') as encoded
,feistel_cipher_63(feistel_cipher_63(values.seq, const.key), const.key, true) as roundtrip
,values.seq - feistel_cipher_63(feistel_cipher_63(values.seq, const.key), const.key, true) as "error"
FROM const CROSS JOIN generate_series(0::bigint,1000) AS values(seq)
UNION ALL
SELECT
values.seq as original
,LPAD(to_hex(feistel_cipher_63(values.seq, const.key)), 16, '0') as encoded
,feistel_cipher_63(feistel_cipher_63(values.seq, const.key), const.key, true) as roundtrip
,values.seq - feistel_cipher_63(feistel_cipher_63(values.seq, const.key), const.key, true) as "error"
FROM const CROSS JOIN generate_series((~(1::bigint<<63)) - 1000, (~(1::bigint<<63)) - 0) AS values(seq)
;WITH const AS (SELECT 6287830383155378919::bigint as key)
SELECT
feistel_cipher_63(0, const.key, true) AS zero,
feistel_cipher_63(1, const.key, true) AS one
FROM const
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment