如何自定义Postgres Pseudo Encrypt函数的输出?

时间:2015-06-07 00:58:38

标签: postgresql encryption

我想在StackOverflow上使用几次提到的pseudo_encrypt函数来使我的ID看起来更随机:https://wiki.postgresql.org/wiki/Pseudo_encrypt

如何自定义此选项以便为我输出唯一的“随机”数字。我在某处读到你可以改变1366.0常数,但我不想冒任何ID冒险,因为任何潜在的ID重复会导致重大问题。

我真的不知道每个常数实际上做了什么,所以除非我得到一些方向,否则我不想乱用它。有谁知道我可以安全地改变哪些常数?

这是:

CREATE OR REPLACE FUNCTION "pseudo_encrypt"("VALUE" int) RETURNS int     IMMUTABLE STRICT AS $function_pseudo_encrypt$
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;
        r1 := l2;
        l1 := r2;
        i := i + 1;
END LOOP;
RETURN ((l1::int << 16) + r1);
END;
$function_pseudo_encrypt$ LANGUAGE plpgsql;

对于bigint的

CREATE OR REPLACE FUNCTION "pseudo_encrypt"("VALUE" bigint) RETURNS bigint IMMUTABLE STRICT AS $function_pseudo_encrypt$
DECLARE
l1 bigint;
l2 bigint;
r1 bigint;
r2 bigint;
i int:=0;
BEGIN
    l1:= ("VALUE" >> 32) & 4294967295::bigint;
    r1:= "VALUE" & 4294967295;
    WHILE i < 3 LOOP
        l2 := r1;
        r2 := l1 # ((((1366.0 * r1 + 150889) % 714025) / 714025.0) * 32767*32767)::bigint;
        r1 := l2;
        l1 := r2;
        i := i + 1;
    END LOOP;
RETURN ((l1::bigint << 32) + r1);
END;
$function_pseudo_encrypt$ LANGUAGE plpgsql;

2 个答案:

答案 0 :(得分:5)

替代解决方案:使用不同的密码

现在可以在postgres wiki上使用其他密码函数。它们会明显变慢,但除此之外,它们更适合生成定制的随机系列独特数字。

对于32位输出,Skip32 in plpgsql将使用10字节宽的密钥加密其输入,因此您只需选择自己的密钥即可拥有自己的特定排列(2 ^ 32的特定顺序)将出现独特的价值观。)

对于64位输出,XTEA in plpgsql的操作类似,但使用16字节宽的键。

否则,只需自定义pseudo_encrypt,请参阅以下内容:

关于pseudo_encrypt实施的说明:

此功能有3个属性

  • 输出值的全局唯一性
  • 可逆性
  • 伪随机效应

第一个和第二个属性来自Feistel网络,正如在@ CodesInChaos的答案中已经解释的那样,它们不依赖于这些常量的选择:1366以及150889和{{ 1}}。

确保在更改714025时,它在数学意义上保持功能,即f(r1)隐含x=y,或者换句话说,相同的输入必须始终产生相同的输出。打破这一点将打破这种独特性。

这些常量和f(x)=f(y)的这个公式的目的是产生一个相当好的伪随机效应。使用postgres内置f(r1)或类似的方法是不可能的,因为它不是如上所述的数学函数。

为什么这些任意常数?在这部分功能中:

random()

公式和值r2 := l1 # ((((1366.0 * r1 + 150889) % 714025) / 714025.0) * 32767)::int; 1366150889来自数字食谱C (1992年,William H.Press,第2版。 ),第7章:随机数,特别是第284和285页。 这本书不能直接在网上索引,但可以通过这里的界面阅读:http://apps.nrbook.com/c/index.html。它也被引用作为实现PRNG的各种源代码的参考。

在本章讨论的算法中,上面使用的算法非常简单且相对有效。从前一个(714025)获取新随机数的公式为:

jran

其中jran = (jran * ia + ic) % im; ran = (float) jran / (float) im; /* normalize into the 0..1 range */ 是当前的随机整数。

此生成器必须在一定数量的值(“句点”)之后循环自身,因此必须仔细选择常量jraniaic期间要尽可能大。这本书提供了一个表p.285,其中建议了不同长度的常数。

imia=1366ic=150889是其中一段时间​​的条目之一  2 29 位,这比需要的还多。

最后乘以im=714025或2 15 -1不是PRNG的一部分,但意味着从0..1伪随机浮点值产生正半整数。除非扩大算法的块大小,否则不要更改该部分。

答案 1 :(得分:3)

此功能看起来像基于Feistel network的阻止程序 - 但它缺少密钥。

Feistel构造是双射的,即它保证没有碰撞。有趣的是:r2 := l1 # f(r1)。只要f(r1)仅取决于r1pseudo_encrypt就会是双射的,无论函数是什么。

缺少密钥意味着任何知道源代码的人都可以恢复顺序ID。因此,您依赖安全性而非默默无闻。

另一种方法是使用一个带密钥的分组密码。对于32位块,选择相对较少,我知道Skip32和ipcrypt。对于64位块,有许多密码可供选择,包括3DES,Blowfish和XTEA