Last active
December 23, 2025 16:35
-
-
Save JasonKleban/b73324a919400e15bf335a4efa75599e to your computer and use it in GitHub Desktop.
Postgres implementation of a 63-bit (unbalanced) feistel cipher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- 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