精华内容
下载资源
问答
  • 伪随机置换
    2021-05-25 02:52:10

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

    无聊,又研究了一下几种排序算法,在测速的时候,发现自己忘记了一个重要的问题,在某天看到有人在帖吧提到生成随机数只计数到32768就停止了,顺手查了一下C库函数的实现,才发现提供的只是0~32767之间的随机数,我去,图省力,直接用标准库去测试算法,实在是,以前自己弄过随机数C++类啊,居然没有拿来用,失算。重新查了一下一些随机数算法,挑了三个流行的常用算法,重新实现伪随机数生成算法库。通过msvc和mingw编译。

    C示例:

    //random.h

    #ifndef __RANDOM_HEADER__

    #define __RANDOM_HEADER__

    #include

    #ifdef _MSC_VER

    #if _MSC_VER < 1600

    typedef unsigned int uint32_t;

    typedef unsigned char uint8_t;

    typedef unsigned __int64 uint64_t;

    typedef __int64 int64_t;

    #else

    #include

    #endif

    #else

    #include

    #endif

    /**

    *三种随机数算法函数库,支持long long随机数生成

    */

    struct tagRand

    {

    void (*seed)(void);// 设置算法种子

    int (*next)(void);// 计算得到一个伪随机数

    int (*range)(int low, int hight);//计算[low, high]之间的伪随机数

    short (*next_short)(void);//返回0-32767之间的伪随机数

    double (*next_double)(void);//随机浮点数

    float (*next_float)(void);//随机浮点数

    int64_t (*next64)(void);// 64位随机数,正负问题自行处理

    void (*set_type)(int type); // 设置使用哪种随机算法type=0~2(lcm, xorshift32, well算法)

    };

    extern struct tagRand Rand;

    #endif

    //random.c

    #include "random.h"

    static uint32_t seed[16];

    static uint64_t seed64;

    static uint32_t get_seed(){ return (unsigned)time(0); }

    // C数据结构一书中线性同余法

    static int lcm()

    {

    //A(48271), M(2147483647), Q=M/A(44488), R=M%A(3399)

    //公式:A*(seed % Q) - R*(seed/Q) 不使用宏,避免宏替换影响int A[]这样的参数

    const int A = 48271, Q=44488, R = 3399;

    *seed = A * (*seed % Q) - R * (*seed / Q);

    return *seed;

    }

    // xorshift 32位算法, 另一种写法,周期较长

    static int xor128()

    {

    uint32_t t=*seed^(*seed<<11);

    *seed=seed[1]; seed[1]=seed[2]; seed[2]=seed[3];

    return seed[3]=seed[3]^(seed[3]>>19)^t^(t>>8) ;

    }

    // xorshift 32位算法

    static int xorshift32()

    {

    uint32_t x = *seed;

    x ^= x << 13;

    x ^= x >> 17;

    x ^= x << 5;

    *seed = x;

    return x;

    }

    // well算法

    static int well512()

    {

    uint32_t a,b,c,d;

    static int index = 0;

    a = seed[index];

    c = seed[(index+13)&15];

    b = a^c^(a<<16)^(c<<15);

    c = seed[(index+9)&15];

    c ^= (c>>11);

    a = seed[index] = b^c;

    d = a ^((a<<5)& 0xDA442D20);

    index = (index + 15)&15;

    a = seed[index];

    seed[index] = a^b^d^(a<<2)^(b<<18)^(c<<28);

    return seed[index];

    }

    // 默认使用算法

    static int (*rands)() = xorshift32;

    //设置使用哪种随机算法

    static void set_algorithm(int type)

    {

    switch(type)

    {

    case 0: rands = lcm;

    case 1: rands = xorshift32;

    case 2: rands = well512;

    }

    }

    // 计算得到一个伪随机数

    static int next(void)

    {

    return rands() & 0x7fffffff;

    }

    //计算[low, high]之间的伪随机数

    static int range_next(int low, int high)

    {

    return low + next() % (high+1-low);

    }

    //返回0-32767之间的伪随机数

    static short next_short(void)

    {

    return next() >> 16;

    }

    //随机浮点数

    static double next_double(void)

    {

    return (double)next() / 0x7fffffff;

    }

    //随机浮点数

    static float next_float(void)

    {

    return (float)next() / 0x7fffffff;

    }

    // 64位随机数,正负问题自行处理

    static int64_t next64(void)

    {

    uint64_t x = seed64;

    x ^= x >> 12;

    x ^= x << 25;

    x ^= x >> 27;

    seed64 = x;

    return x * 0x2545F4914F6CDD1D;

    }

    // 设置算法种子

    static void seed_rand(void)

    {

    int i;

    for( i = 0; i <8; ++i){

    seed[i] = (get_seed() << (i+1))-1;

    seed[i+1] = get_seed()>>(i+1);

    }

    seed64 = *seed;

    seed64 = (seed64 << 32) | seed[next() & 15];

    }

    // 初始化结构体

    struct tagRand Rand = {

    seed_rand, next, range_next,

    next_short, next_double, next_float, next64,

    set_algorithm

    };

    //测试程序:

    #include

    #include "random.h"

    int main()

    {

    int i, size = 15;

    Rand.set_type(2);

    Rand.seed();

    //随机整数测试

    for(i = 0; i < size; ++i)

    printf("%d ", Rand.next()%10);

    putchar('\n');

    //范围测试

    for(i = 0; i < size; ++i)

    printf("%c ", Rand.range('A', 'Z'));

    putchar('\n');

    for(i = 0; i < size; ++i)

    printf("%c ", Rand.range('a', 'z'));

    putchar('\n');

    //浮点数测试

    for(i = 0 ; i < 10; ++i)

    printf("%f\n", Rand.next_float());

    //64位数测试

    for(i = 0; i < 3; ++i)

    printf("%I64d\n", Rand.next64());

    //for(i=0; i < size; ++i)

    //printf("%c ", rand_char());

    //putchar('\n');

    printf("sizeof rand:%d\n", sizeof(Rand));

    }

    运行结果:

    0 7 1 7 0 1 9 2 5 6 2 3 1 2 9

    E A P S W U T S V B S U Z W X

    t f u n d c m s c z e y r f s

    0.389461

    0.888160

    0.725454

    0.885750

    0.817395

    0.770790

    0.688366

    0.003831

    0.185819

    0.965431

    135809110265936984

    -2632048472102704751

    6521441842461015894

    sizeof rand:32

    请按任意键继续. . .

    更多相关内容
  • 针对路网连续查询用户的位置隐私和查询内容隐私保护问题,提出一种基于伪随机置换的隐私保护方法。首先,基于路网顶点(锚点)组织兴趣点(PoI)分布信息,以单个路网顶点为基本处理对象,构造基于伪随机置换的LBS...
  • 密码学中遇到的问题 伪随机置换、伪随机排列有区别吗,在Google Translate中都是pseudorandom permutation, 都相当于是伪随机双射吗?
  • C语言的伪随机

    2021-05-25 04:51:43
    一直想好好的系统的学习一下C语言的伪随机数,今天终于逮到机会了伪随机数C语言中有可以产生随机数据的函数,需要添加stdlib.h和time.h头文件。首先在main函数开头加上srand(unsigned)time(NULL))。先来介绍一下...

    一直想好好的系统的学习一下C语言的伪随机数,今天终于逮到机会了

    伪随机数

    C语言中有可以产生随机数据的函数,需要添加stdlib.h和time.h头文件。首先在main函数开头加上srand(unsigned)time(NULL))。

    先来介绍一下srand

    头文件:

    定义函数:void srand (unsigned int seed);

    函数说明:srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,通常可以利用getpid()或time(0)的返回值来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。

    再来介绍一下time,

    函数名称: time

    函数原型: time_t time(time_t *timer)

    函数功能: 得到机器的日历时间或者设置日历时间

    函数返回: 机器日历时间

    参数说明: timer=NULL时得到机器日历时间,timer=时间数值时,用于设置日历时间,time_t是一个long类型

    所属文件:

    因此上述的srand((unsigned)time(NULL))是利用系统时间来初始化随机种子的。

    最后来介绍一下重要的一个函数rand

    rand函数

    头文件:

    定义函数:int rand(void)

    函数功能:产生随机数

    函数说明:因为rand的内部实现是用线性同余法做的,它不是真的随机数,只不过是因为其周期特别长,所以,在一定的范围里可看成是随机的。rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。

    返回值: 返回0至RAND_MAX之间的随机整数值,RAND_MAX的范围最少是在32767之间(int),即双字节(16位数)。若用unsigned int 双字节是65535,四字节是4294967295的整数范围。且0-RAND_MAX每个数字被选中的机率是相同的。

    rand()产生的是假随机数字,每次执行时是相同的。若要不同,以不同的值来初始化它.初始化的函数就是srand()。

    举个例子

    #include

    #include

    #include

    int main(){

    srand((unsigned)time(NULL));

    for(int i=0;i<10;i++){

    printf("%d ",rand());

    }

    return 0;

    }

    e46faef3910f34fcfc167799b4a1f520.png

    说白了上述的随机数范围是0~32767之间,若没有设置系统时间种子,则rand默认随机种子为1,则多次运行结果都是一致的。

    当然,若想要一个给定范围的随机数,则需要使用rand()%(b-a+1)+a。显然rand()%(b-a+1)的范围是[0,b-a],再加上a即为[a,b]。

    举个例子([0,1]和[3,6]之间的随机数)

    #include

    #include

    #include

    int main(){

    srand((unsigned)time(NULL));

    for(int i=0;i<10;i++){

    printf("%d ",rand()%2);

    }

    putchar('\n') ;

    for(int i=0;i<10;i++){

    printf("%d ",rand()%4+3);

    }

    return 0;

    }

    7686c3f7b128c6ce79e9608e42ca2b25.png

    一般来说这个范围最大是0~32767,若想要生成超过32767的更大的随机数,则可以采用,移位或者拼凑或者用随机数除以RAND_MAX,这样就会得到一个[0,1]范围内的浮点数。只需要这个浮点数乘以范围长度(b-a+1)再加上a即可。即(int)((double)rand()/32767*(b-a+1)+a),相当于这个浮点数在[a,b]范围内的比例位置

    举个例子

    #include

    #include

    #include

    int main(){

    srand((unsigned)time(NULL));

    for(int i=0;i<10;i++){

    printf("%d ",(int)((1.0*rand()/RAND_MAX*50000+10000)));//10000~60000

    }

    return 0;

    }

    7952385b8499b1771a1417d409bf2e35.png

    PS:介绍一个简单的AC技巧,在程序中一次性计算出所有需要用到的结果,然后查询直接取这些结果,典型的AC技巧有木有(●ˇ∀ˇ●)

    小结

    C语言的伪随机数适用范围还是挺广泛的,下次需要用起来的时候,就不需要再去找什么资料了。

    展开全文
  • 伪随机置换 PRP的正式定义如下: 对于一类带密钥的置换PPP,有P:{0,1}∗×{0,1}n→{0,1}nP:{0,1}∗×{0,1}n→{0,1}nP: {0,1}^{*} \times {0, 1}^{n} \rightarrow {0, 1}^{n},即y←P(k,x)y←P(k,x)y \leftarrow P(k...

    Python微信订餐小程序课程视频

    https://edu.csdn.net/course/detail/36074

    Python实战量化交易理财系统

    https://edu.csdn.net/course/detail/35475
    本文将介绍密码学中的PRF、PRP等相关概念,并介绍 PRP/PRF 转换引理及其证明,希望读完本文后,你能对现代密码学中这几个基础概念有所了解。

    在开始本文前,希望你有如下预备知识:

    • 现代密码学是怎样的一门学科?
    • “Security Through Obscurity” 是什么意思?
    • 集合、极限、函数、随机变量、采样等数学概念是什么?

    概述

    在之前的一篇博客中提到,伪随机性是现代密码学的根基,每一个密码算法都需要有这样的伪随机性来源。而伪随机数发生器只是一个承载伪随机性的初等原语,其数学性质和模型都太朴素,不足以帮助我们构建更复杂的算法结构。

    伪随机函数的出现,对「如何将随机性这个特点与函数的输入输出结合?」这一问题给出了严格的数学定义与描述方法。这为各位密码学家们提供了一个很强的模型。进一步地,伪随机置换则在此基础上,引入了「映射可逆」的概念,从而丰富和细化了PRF的抽象能力。

    这篇博客将依次介绍伪随机数发生器(PRNG)、伪随机函数(PRF)、伪随机置换(PRP),并给出大名鼎鼎的 PRF/PRP 转换引理及其证明。

    PRNG

    伪随机性之于现代密码学的重要性在之前的博客中已经为大家介绍过了,而作为伪随机性的载体,伪随机数发生器(Pseudorandom Number Generator,PRNG)的定义重新陈述如下:

    令GGG为一多项式时间算法,其输入为s∈{0,1}ns∈{0,1}ns\in {0, 1}^{n},即为一长度为nnn的01比特串,输出的长度记作l(n)l(n)l(n)。其中,l(⋅)l(⋅)l(\cdot )也为一多项式时间算法,l(n)l(n)l(n)则表示是nnn的多项式界(如 n2n2n^{2}、nnn)下的一个数值。若GGG为一PRNG,则应同时满足如下两个条件:

    • 对于任意的nnn,都有l(n)>nl(n)>nl(n) > n;
    • 若此时对于任意一具有多项式资源的敌手AA\mathcal{A},都存在一个可忽略(negligible)的概率 ϵϵ\epsilon ,使得下式成立:

    |Pr[A®=1]−Pr[A(G(s))=1]|≤ϵ(1)(1)|Pr[A®=1]−Pr[A(G(s))=1]|≤ϵ|\mathrm{Pr}[\mathcal{A}®=1] - \mathrm{Pr}[\mathcal{A}(G(s))=1]| \le \epsilon \tag{1}
    PRNG作为伪随机性最直观的抽象模型,示意图如下图所示。

    可以看到,PRNG在算法构建上的表现力似乎还不是很强。例如,G(s)G(s)G(s)作为PRNG的输出,但这和一个密码算法到底有啥关系?生成密钥?可一个算法又不只有密钥,还有输入输出呢!因此,PRNG作为一个初等原语,其抽象能力还是有些弱,这就需要表达能力更强的原语了。

    PRF与PRP

    PRF

    伪随机函数(即 Pseudorandom Function, PRF)与PRNG相比,多了对输入输出的描述,而这正是「函数」的意义。在介绍PRF之前,首先介绍随机函数。

    随机函数

    对于将nnn位比特串{0,1}n{0,1}n{0, 1 }^{n}映射到nnn位比特串{0,1}n{0,1}n{0, 1 }^{n}的所有函数组成的函数族FF\mathcal{F},一个随机函数fff是指在FF\mathcal{F}中以均匀随机采样的方法选择得到的一个函数,即有f∈rndFf∈rndFf \stackrel{rnd} \in \mathcal{F}.

    在该函数族中,一个函数相当于给出了一种2n2n2{n}个nnn比特长的串的排列,那么整个函数族的大小即为2n⋅2n2n⋅2n2{n\cdot 2^{n}}。这是一个指数级别的计算规模,因此即使存在一个多项式资源的敌手或区分器,他也无法在多项式时间内建模某个随机函数fff的映射方式。

    伪随机函数

    PRF的正式定义如下:

    对于一类带密钥的函数FFF,有F:{0,1}∗×{0,1}∗→{0,1}∗F:{0,1}∗×{0,1}∗→{0,1}∗F: {0,1}^{} \times {0, 1}^{} \rightarrow {0, 1}^{*},即y←F(k,x)y←F(k,x)y \leftarrow F(k, x),其中xxx为输入,kkk为密钥,yyy为输出。若称FFF为PRF,则可满足以下条件:

    • 高效计算:给定kkk与xxx,存在高效的多项式时间算法能计算y=F(k,x)y=F(k,x)y=F(k, x);
    • 不可区分:随机选定密钥kkk,伪随机函数F(k,⋅)F(k,⋅)F(k, \cdot)与一随机函数f(⋅)f(⋅)f(\cdot)是不可区分的;
    • 长度保留:y、x、ky、x、ky、x、k的长度均为nnn;该性质是非必需的。

    可以看到,PRF的性质和很多密码算法都有共性了:接收一个输入,返回一个看起来是随机输出的函数,而这种伪随机性来自于密钥,只要密钥kkk不被敌手获取,F(k,x)F(k,x)F(k, x)的映射性质就无法被建模或攻破。因此,在有些地方,PRF会被定义成「在函数族F中F中\mathcal{F}中,由密钥kkk作为索引的随机函数」,记作Fk(⋅)Fk(⋅)F_{k}(\cdot)。

    综上,两种PRF定义都可以用上面的示意图来表示。而不论哪种定义,PRF都会涉及密钥、输入、输出三个要素,这正好可以与常见的密码算法是相同的,消息认证算法就是一种PRF,对称加密算法在广义上也是一种PRF。在PRF的模型下,我们似乎可以刻画出更多的安全性质,例如输出的不可区分性,输出的单向性等等。PRF丰富了伪随机性的范围和尺度,让其能更好地贴合人们的直觉。

    尽管PRF能帮助我们抽象不同的密码算法,但是却不能很好地刻画输入与输出之间的映射关系。没错,借助PRF的确能构建出xxx到yyy的映射关系,但对加解密算法而言,xxx与yyy之间需要是严格的双射。因此,为进一步加强PRF对这类「可逆」密码算法的表现能力,一种新的原语出现了。

    PRP

    伪随机置换(即 Pseudorandom Permutation,PRP)与PRF相比,多了对xxx到yyy映射关系可逆的要求,而这也是「置换」的意义。同样地,此处将先介绍随机置换。

    随机置换

    对于将nnn位比特串{0,1}n{0,1}n{0, 1 }^{n}映射到自身的所有置换组成的置换族PP\mathcal{P},一个随机置换ppp是指在PP\mathcal{P}中以均匀随机采样的方法选择得到的一个置换,即有p∈rndPp∈rndPp \stackrel{rnd} \in \mathcal{P}.

    同理,由于置换一定是双射的,因此该置换族的大小等价于所有输出的全排列数量,即为(2n)!(2n)!(2^{n})!;但这同样是一个远大于多项式(super poly)级别的计算规模,因此即使存在一个多项式资源的敌手或区分器,他也无法在猜出或建模出某个随机置换ppp的映射方式。

    伪随机置换

    PRP的正式定义如下:

    对于一类带密钥的置换PPP,有P:{0,1}∗×{0,1}n→{0,1}nP:{0,1}∗×{0,1}n→{0,1}nP: {0,1}^{*} \times {0, 1}^{n} \rightarrow {0, 1}^{n},即y←P(k,x)y←P(k,x)y \leftarrow P(k, x),其中xxx为输入,kkk为密钥,yyy为xxx对应的置换后结果。若称PPP为PRP,则可满足以下条件:

    • 高效计算:给定kkk与xxx,存在高效的多项式时间算法能计算y=P(k,x)y=P(k,x)y=P(k, x);给定kkk与yyy,存在高效的多项式时间可逆算法能计算x=P−1(k,y)x=P−1(k,y)x=P^{-1}(k, y)
    • 不可区分:随机选定密钥kkk,伪随机置换P(k,⋅)P(k,⋅)P(k, \cdot)与一随机置换p(⋅)p(⋅)p(\cdot)是不可区分的;
    • 长度保留:y、xy、xy、x的长度均为nnn。

    可以看到,与PRF相比,PRP的定义实质上就是将原来的「函数」变为「置换」,即xxx与yyy此时位于同一概率空间中,之间的映射关系为双射,示意图如下图所示。PRP对于那些具备逆运算的密码算法而言,能更好地描述输入和输出之间的可逆映射关系,因此更适合用于刻画加解密算法时。至此,现代密码学中三个非常重要的原语已经全部介绍完毕。

    不可区分性

    上文介绍PRF与PRP时,未阐述究竟是如何不可区分的,但这一性质实际上是密码学计算安全性的核心,本节将予以重点介绍。

    PRF的不可区分性

    一个长度保留的、可高效计算的PRF Fk(⋅)Fk(⋅)F_{k}(\cdot)的不可区分性表示为,对于任意的具有多项式规模资源的敌手AA\mathcal{A},都存在一个可忽略的极小概率ϵ(n)ϵ(n)\epsilon(n),使得下式成立:

    |Pr[AFk(⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|≤ϵ(n)(2)(2)|Pr[AFk(⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|≤ϵ(n)|\mathrm{Pr}[\mathcal{A}{F_{k}(\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1]| \le \epsilon(n) \tag{2}
    其中ϵ(n)ϵ(n)\epsilon(n)是指与长度参数nnn相关的一个可忽略概率,例如12n12n\frac{1}{2{n}};AFk(⋅)(1n)=1AFk(⋅)(1n)=1\mathcal{A}{F_{k}(\cdot)}(1{n})=1以及Af(⋅)(1n)=1Af(⋅)(1n)=1\mathcal{A}{f(\cdot)}(1{n})=1分别表示敌手AA\mathcal{A}在未知当前函数是Fk(⋅)Fk(⋅)F_{k}(\cdot)或f(⋅)f(⋅)f(\cdot)的条件下,正确猜出了这个函数是什么。1n1n1{n}表示函数参数规模为nnn。因此,式(2)可以写作:

    |Pr[A认为当前函数是伪随机函数|当前函数是伪随机函数]−Pr[A认为当前函数是随机函数|当前函数是随机函数]|≤ϵ|Pr[A认为当前函数是伪随机函数|当前函数是伪随机函数]−Pr[A认为当前函数是随机函数|当前函数是随机函数]|≤ϵ|\mathrm{Pr}[\mathcal{A} 认为当前函数是伪随机函数|当前函数是伪随机函数] \

    • \mathrm{Pr}[\mathcal{A} 认为当前函数是随机函数|当前函数是随机函数]| \le \epsilon

    实际上,敌手AA\mathcal{A}在辨别当前函数是谁的这种情景,在现代密码学的语义下,通常称敌手AA\mathcal{A}与一个寓言机(Oracle)OO\mathcal{O}进行询问(query)游戏(Game),OO\mathcal{O}的内部可能是Fk(⋅)Fk(⋅)F_{k}(\cdot)或f(⋅)f(⋅)f(\cdot)。而根据每次询问OO\mathcal{O}返回的输出,AA\mathcal{A}最终输出对OO\mathcal{O}的猜测结果。示意图如下所示。

    如图所示,敌手AA\mathcal{A}在与OO\mathcal{O}询问若干次后,若能正确判定自己面前的这个OO\mathcal{O}的内部究竟是一个伪随机函数还是一个随机函数,那就称AA\mathcal{A}攻破了PRF的不可区分性。换言之:

    • 当敌手能轻易区分何时是PRF,何时是随机函数时,说明敌手攻破PRF的优势足够
    • 当敌手对这二者是不可区分时,说明敌手攻破PRF的优势很

    因此,敌手的这种区分能力其实就是敌手攻破PRF的优势,那么式(2)还可以写作:

    AdvPRFO(A)=|Pr[AFk(⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|≤ϵ(n)AdvOPRF(A)=|Pr[AFk(⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|≤ϵ(n)\mathrm{Adv}_{\mathcal{O}}{PRF}(\mathcal{A})=|\mathrm{Pr}[\mathcal{A}{F_{k}(\cdot)}(1^{n})=1] - \mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1]| \le \epsilon(n)
    其中,AdvPRFO(A)AdvOPRF(A)\mathrm{Adv}_{\mathcal{O}}^{PRF}(\mathcal{A})即为敌手AA\mathcal{A}面对寓言机OO\mathcal{O}时进行不可区分实验的优势。

    PRP的不可区分性

    一个长度保留的、可高效计算的PRP Pk(⋅)Pk(⋅)P_{k}(\cdot)的不可区分性表示为,对于任意的具有多项式规模资源的敌手 AA\mathcal{A},都存在一个可忽略的极小概率 ϵ(n)ϵ(n)\epsilon(n),使得下式成立:

    |Pr[APk(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤ϵ(n)(3)(3)|Pr[APk(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤ϵ(n)|\mathrm{Pr}[\mathcal{A}{P_{k}(\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1]| \le \epsilon(n) \tag{3}
    同理,APk(⋅)(1n)=1APk(⋅)(1n)=1\mathcal{A}{P_{k}(\cdot)}(1{n})=1以及Ap(⋅)(1n)=1Ap(⋅)(1n)=1\mathcal{A}{p(\cdot)}(1{n})=1分别表示敌手AA\mathcal{A}在当前的寓言机OO\mathcal{O}的确是Pk(⋅)Pk(⋅)P_{k}(\cdot)或p(⋅)p(⋅)p(\cdot)的条件下,正确猜出了这个置换是什么。示意图如下图所示。

    在PRP不可区分实验中,引入敌手的优势改写式(3)如下:

    AdvPRPO(A)=|Pr[APk(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤ϵ(n)AdvOPRP(A)=|Pr[APk(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤ϵ(n)\mathrm{Adv}_{\mathcal{O}}{PRP}(\mathcal{A})=|\mathrm{Pr}[\mathcal{A}{P_{k}(\cdot)}(1^{n})=1] - \mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1]| \le \epsilon(n)

    PRP/PRF转换引理与证明

    这个引理能做什么?

    一个安全的加解密算法E=(E,D)E=(E,D)\mathcal{E}=(E, D)可以抽象成一个实现了PRP的寓言机。由于在对称算法的设计中,EEE与DDD的算法结构通常是对合的(Involution)。因此在证明EE\mathcal{E}的安全性时,只考虑加密算法EEE即可。这时,如果EEE是安全的,则既可被证明是一个PRP寓言机,也可被证明是一个PRF寓言机。证明EEE的安全性,也就是回答下面两个问题之一:

    • 能否证明EEE是一个PRP(即AdvPRPE(A)AdvEPRP(A)\mathrm{Adv}_{E}^{PRP}(\mathcal{A})是否为可忽略的?)
    • 能否证明EEE是一个PRF(即AdvPRFE(A)AdvEPRF(A)\mathrm{Adv}_{E}^{PRF}(\mathcal{A})是否为可忽略的?)

    而许多时候,PRP所代表的这种「置换」模型的安全性并不利于证明工作的推进,如果将其归约到PRF这种更符合一般数学直觉的「函数」模型上,概率空间的定义、碰撞事件等问题都可以在函数的框架下进行计算和讨论,这为证明的抽象和表示带来了方便。

    因此,在加解密算法尤其是分组密码的安全性证明中,该引理能将证明工作的重心转换到更为熟悉和直观的「函数」模型上。

    PRP/PRF转换引理

    铺垫了这么多,终于该介绍这个转换引理的内容了。

    令AA\mathcal{A}为一具有多项式资源的敌手,AA\mathcal{A}对寓言机OO\mathcal{O}最多能进行qqq次询问,OO\mathcal{O}内部可能实现了一个随机函数fff,也可能实现了一个随机置换ppp。对于整数n≥1n≥1n \ge 1,则下式[1]成立:

    |Pr[Af(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤q(q−1)2n+1(4)(4)|Pr[Af(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤q(q−1)2n+1|\mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1]| \le \frac{q(q - 1)}{2^{n+1}} \tag{4}
    通常,我们认为敌手不会发送两次相同的询问。若考虑到敌手优势的定义,则有:

    |AdvPRPE(A)−AdvPRFE(A)|=∣∣|Pr[AE(k,⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|−|Pr[AE(k,⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|∣∣≤|Pr[Af(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]||AdvEPRP(A)−AdvEPRF(A)|=||Pr[AE(k,⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|−|Pr[AE(k,⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]||≤|Pr[Af(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|\begin{align*}
    |\mathrm{Adv}_{E}^{PRP}(\mathcal{A})- \mathrm{Adv}_{E}^{PRF}(\mathcal{A})|
    &= \left| |\mathrm{Pr}[\mathcal{A}{E(k,\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1]|-|\mathrm{Pr}[\mathcal{A}^{E(k, \cdot)}(1^{n})=1] - \mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1]|\right| \
    &\le |\mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1]| \
    \end{align*}
    因此该引理还有另外一种写法[2]:

    |AdvPRPE(A)−AdvPRFE(A)|≤q(q−1)2n+1(5)(5)|AdvEPRP(A)−AdvEPRF(A)|≤q(q−1)2n+1|\mathrm{Adv}_{E}^{PRP}(\mathcal{A})- \mathrm{Adv}_{E}^{PRF}(\mathcal{A})| \le \frac{q(q-1)}{2^{n+1}} \tag{5}
    这两种形式均是正确的,使用时可根据上下文需求选择。可以看到, 式(4)表示敌手区分随机函数和随机置换的优势是一与询问次数qqq和nnn有关的值;当nnn取较大值如128时,这一上界q(q−1)2n+1q(q−1)2n+1\frac{q(q-1)}{2^{n+1}}变成为了可忽略的值,即当算法规模相对询问次数足够大时,敌手的区分能力将会很快减弱。

    引理证明

    为证明这一引理,我们定义事件 CollColl\mathrm{Coll} 为:当敌手AA\mathcal{A}提交不同的输出xi,xjxi,xjx_{i},x_{j}给寓言机OO\mathcal{O}后,OO\mathcal{O}返回了相同的输出yyy。而事件 DistDist\mathrm{Dist} 为CollColl\mathrm{Coll}的补集,即 Pr[Dist]=1−Pr[Coll]Pr[Dist]=1−Pr[Coll]\mathrm{Pr[Dist]}=1-\mathrm{Pr[Coll]}. 首先,我们说明这一结论:

    Pr[Ap(⋅)(1n)=1]=PrAf(⋅)(1n)=1|Dist(6)Pr[Ap(⋅)(1n)=1]=Pr[Af(⋅)(1n)=1|Dist]\mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1] = \mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1 | \mathrm{Dist} ] \tag{6}
    式(6)说明,当敌手的查询结果未发生碰撞时,敌手是无法区分一个随机函数和一个随机置换的。回顾PRF与PRP的定义,二者最大的区别就在于输入与输出所定义的概率空间不同。在PRP这种「置换」定义下,输入与输出为同一概率空间,这也就暗含了输入元素和输出元素必然为一一对应的双射关系;而在PRF这种「函数」定义下,输入与输出为不同空间,此时多个输入可能对应同一输出。

    因此,如果已经先验地确定了输出不会发生碰撞,那么函数寓言机将「看起来」与置换寓言机完全一样,从而式(6)成立。这一结论的其他更严格的证明可参照xxx。

    当式(6)成立时,令x=Pr[Ap(⋅)(1n)=1]=Pr[Af(⋅)(1n)=1|Dist]x=Pr[Ap(⋅)(1n)=1]=Pr[Af(⋅)(1n)=1|Dist]x=\mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1] = \mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1 | \mathrm{Dist} ],令y=Pr[Af(⋅)(1n)=1|Coll]y=Pr[Af(⋅)(1n)=1|Coll]y=\mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1 | \mathrm{Coll}],根据全概率公式,可得以下结论:

    |Pr[Ap(⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|=∣∣x−Pr[Af(⋅)(1n)=1|Dist]Pr[Dist]−Pr[Af(⋅)(1n)=1|Coll]Pr[Coll]∣∣=|x−xPr[Dist]−yPr[Coll]|=|x−x+xPr[Coll]−yPr[Coll]|=|(x−y)Pr[Coll]|≤PrColl|Pr[Ap(⋅)(1n)=1]−Pr[Af(⋅)(1n)=1]|=|x−Pr[Af(⋅)(1n)=1|Dist]Pr[Dist]−Pr[Af(⋅)(1n)=1|Coll]Pr[Coll]|=|x−xPr[Dist]−yPr[Coll]|=|x−x+xPr[Coll]−yPr[Coll]|=|(x−y)Pr[Coll]|(7)≤Pr[Coll]\begin{align*}
    |\mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1]|&=\left|x-\mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1|\mathrm{Dist}]\mathrm{Pr[Dist]}-\mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1|\mathrm{Coll}]\mathrm{Pr[Coll]}\right| \
    &=|x-x\mathrm{Pr[Dist]}-y\mathrm{Pr[Coll]}|\
    &=|x-x+x\mathrm{Pr[Coll]}-y\mathrm{Pr[Coll]}| \
    &=|(x-y)\mathrm{Pr[Coll]}|\
    &\le \mathrm{Pr[Coll]} \tag{7}
    \end{align*}
    式(7)的推导说明了此时敌手AA\mathcal{A}的区分能力不会超过 CollColl\mathrm{Coll} 事件的发生概率,也就意味着「除非让AA\mathcal{A}在不同的询问中获得了相同的结果,否则AA\mathcal{A}是不可能知道这是一个随机函数还是一个随机置换的」。这个结论在直觉上也是符合「函数」与「置换」的定义的。

    因此,CollColl\mathrm{Coll} 事件若发生,则说明AA\mathcal{A}在提交的qqq次询问中,OO\mathcal{O}有两次选取了同一个yyy进行返回,由于函数模型下的OO\mathcal{O}是没有记忆性的(类似古典概型中的小球取出会放回),因此返回同一个yyy发生的概率是12n12n\frac{1}{2^{n}}。另一方面,qqq次询问一共会产生(q2)(q2)\binom{q}{2}个(xi,xj)(xi,xj)(x_{i}, x_{j})组合对。综上可知,Pr[Coll]≤(q2)12n=q(q−1)2n+1Pr[Coll]≤(q2)12n=q(q−1)2n+1\mathrm{Pr[Coll]}\le \binom{q}{2}\frac{1}{2{n}}=\frac{q(q-1)}{2{n+1}}. 证毕。

    不同的界

    其实,在许多地方,式(4)(5)的不等式上界会写为q22n+1q22n+1\frac{q{2}}{2{n+1}},即为[3]:

    |AdvPRPE(A)−AdvPRFE(A)|≤|Pr[Af(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤q22n+1(8)(8)|AdvEPRP(A)−AdvEPRF(A)|≤|Pr[Af(⋅)(1n)=1]−Pr[Ap(⋅)(1n)=1]|≤q22n+1|\mathrm{Adv}_{E}^{PRP}(\mathcal{A})- \mathrm{Adv}_{E}^{PRF}(\mathcal{A})| \le |\mathrm{Pr}[\mathcal{A}{f(\cdot)}(1{n})=1] - \mathrm{Pr}[\mathcal{A}{p(\cdot)}(1{n})=1]| \le \frac{q{2}}{2{n+1}} \tag{8}
    由先前的证明过程可知,上界q(q−1)2n+1q(q−1)2n+1\frac{q(q-1)}{2{n+1}}若能成立,则自然蕴含q22n+1q22n+1\frac{q{2}}{2^{n+1}}也是成立的。因此两种版本的引理公式均是正确的。在许多论文中,式(8)其实是更常见的写法,这是因为平方式的形式更利于表达和记忆。两种上界之所以能共存,是由于前者更严密(tight),后者更好记,在使用时按需选择即可。

    关于这一不同的上界,更多信息可以参见这一解答

    总结

    PRP/PRF转换引理其实只是许多复杂证明的第一步,但其「只要没碰撞,敌手就分不出来」这一核心思想在对称算法的证明中却十分重要。该引理说明了如果一个加密算法是PRP,那它必然也是一个PRF,这一基础推论应能在证明中灵活应用。此外,如果见到上界不同的两个引理版本,请不要惊慌,两个版本都是正确的,可按需使用。最后,本文的所有重点总结如下:

    • PRF与PRP的定义和性质
    • 不可区分性的本质
    • PRP/PRF转换引理内容
    • 碰撞事件的内涵及其概率计算

    感谢你的阅读,欢迎给出建议,以一句歌词作为结束。

    “劳斯难面对,却跟她勾过手指;莱斯,偏偏那样痴” —— 黄伟文《劳斯·莱斯》

    参考

    [1] https://eprint.iacr.org/2004/331.pdf
    [2] https://crypto.stanford.edu/~dabo/cs255/lectures/PRP-PRF.pdf
    [3] https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_5.pdf

    展开全文
  • PRNG伪随机数的破解方法

    千次阅读 2021-10-21 17:55:47
    先简单的介绍一下PRNG伪随机数生成算法,网上的教程好多都好复杂,其实对于我们做这道题来讲用不到那么多,只讲最核心的就行: 原理公式:注意那个和,这个其实表示上一次生成的随机数会作用到下一次的随机数生成...

    一、简单的PRNG算法简介 

    先简单的介绍一下PRNG伪随机数生成算法,网上的教程好多都好复杂,其实对于我们做这道题来讲用不到那么多,只讲最核心的就行:

    原理公式:注意那个x_{n+1}x_{n }^{},这个其实表示上一次生成的随机数会作用到下一次的随机数生成过程中。

      

    写一段很简单的python代码:

    seed=*****     #初始随机数种子
    Randrom_Num=[]  #生成的所有随机数
    for i in range(0,n):   #表示生成n个随机数
         seed = (seed * a + b) % n   #这个新的seed计算后就是生成的随机数
         Randr_Num.append(seed)
    
    

    这就给了我们破解这类题的思路。见下面。 

    Crypto题目如下。

    from Crypto.Util.number import bytes_to_long, getPrime
    from flag import flag
    a = getPrime(400)
    b = getPrime(400)
    n = getPrime(400)
    flag = bytes_to_long(flag)
    seed = flag
    result = []
    for i in range(3):
        seed = (seed * a + b) % n
        result.append(seed)
    print(result)
    print(n)
    """
    [1633086272556604467630727869278140040711140555507257984778706962389364338237377391272504059109316040445365656669071569236, 1206389051656797336925675372412697477889689141174380289961348552709531162180853687116202278892215286522581909284859193494, 664238088423928246579566483655935746695807924062694495126404306361290788561253706421181510449476188038387172722467882193]
    2482696698513566590184292572066254640333143735400366745928208268241117181592178071999744746850718587310205478604372055081
     
    """

    二、解密过程

    想要解密的话必须有连续生成的三个随机数和模数才可以。 

    三个随机数分别是lcd0,lcd1,lcd2。然后根据生成算法可得下面的一个方程组。

    lcg1=(a*lcg0+c)%m      

    lcg2=(a*lcg1+c)%m

    两式相减  

    左边为:lcg2-lcg1   右边为:(a*lcg1+c)-(a*lcg0+c)

    化简为: [lcg2-lcg1=a*(lcg1-lcg0) ] %m

    根据求余运算规则可知
    [a=(lcg2-lcg1)*gmpy2.invert(lcg1-lcg0,m)]%m
    此时代入lcg0,lcg1,lcg2之后可求得a

    代入lcg1=(a*lcg0+c)%m,把c当作未知数放到等式左边后,式子为:
    c=[lcg1-(a*lcg0)]%m


    #如果怕自己解到的a,c是错的话可以带入第三个式子去试试。
    # 类推lcg3_guess=(a*lcg2+c)%m
    # 将求得地lcg3_guess与lcg3比较,如果相等,证明求得的a,c,m为正确值
    # 否则,m=m+1,继续验证

    而一般情况下会把flag当作lcd0输入,所以还需要我们解一元同余式来求最后的flag。

    求解一元同余式的方法:照着公式编程就行。下面找的是一次同余式的通解,但是在ctf比赛中实际上只有一个flag,所以只有一个解,因此(a,m)就是1,这样编程的时候公式可以简略好多。

    三.写出解密脚本 

    import gmpy2
    from Crypto.Util.number import bytes_to_long, getPrime, long_to_bytes
    
    n=2482696698513566590184292572066254640333143735400366745928208268241117181592178071999744746850718587310205478604372055081
    result=[1633086272556604467630727869278140040711140555507257984778706962389364338237377391272504059109316040445365656669071569236, 1206389051656797336925675372412697477889689141174380289961348552709531162180853687116202278892215286522581909284859193494, 664238088423928246579566483655935746695807924062694495126404306361290788561253706421181510449476188038387172722467882193]
    
    a=((result[2]-result[1])*gmpy2.invert(result[1]-result[0],n))%n
    b=(result[1]-(a*result[0]))%n
    
    #求解一元同余式得到flag
    flag=(((result[0]-b))*gmpy2.invert(a,n)+n)%n
    
    print(long_to_bytes(flag))
    
    

    展开全文
  • 本章着重介绍“伪随机性”,并基于完善保密加密的约束条件,给出用短密钥加密很长消息的方案,实现牺牲部分计算安全但已经足够的安全性。 3.1 密码学的计算方法 第二章的方案叫做“信息理论安全”,其安全性基于敌手...
  • CTF之java伪随机数random函数破解 预测

    千次阅读 2020-10-25 17:02:38
    某个后台访问之后,首先映入眼帘的只有一个登录框 抓包如下: ...因为有源码,看看源码怎么说: 大概意思就是当cookie_token不等于空...2、搞定这个伪随机 在师傅疯狂暗示下,我放弃了暴力美学,走上了第二条路,有大佬
  • 运用排列熵算法分析了离散混沌系统产生的混沌序列和混沌伪随机序列的复杂性,讨论了混沌系统参数对序列复杂性的影响情况。研究表明:多次粗粒化后得到的混沌伪随机序列保持了原有混沌序列的复杂性特点;与Logistic系统...
  • 现代密码学3.5--伪随机函数/PRF

    千次阅读 2021-11-25 18:45:45
    现代密码学3.5--伪随机函数/PRF 博主正在学习INTRODUCTION TO MODERN CRYPTOGRAPHY (Second Edition) --Jonathan Katz, Yehuda Lindell,做一些笔记供自己回忆,如有错误请指正。整理成一个系列现代密码学,方便检索...
  • 伪随机

    2020-11-12 15:02:05
    在这之前了解了伪随机性的概念,也用伪随机发生器、伪随机函数和伪随机置换这333个原语构造过安全的加密方案,但是却对这些原语的构造没有什么了解。这次就从单向函数存在这个假设出发,构造这些原语。这样,安全的...
  • 现代密码学(二)对称加密算法

    千次阅读 2021-06-30 21:03:55
    文章目录 计算性安全 对称加密的计算性安全 语义安全 构造安全的加密方案 伪随机发生器 (PRG, pseudorandom generator) 流密码 Proof by Reduction 构造计算性安全的加密方案 更强的安全概念 多明文下的窃听实验...
  • 伪随机函数 java实现

    2018-06-19 16:23:47
    public class RandomCy { private static final long multiplier = 0x5DEECE66DL; private static final long addend = 0xBL; private static final long mask = (1L &... /** * 默认的随机种子 是 -1 */ pr...
  • 密码学(四)

    2022-09-07 00:31:14
    在Feistel结构中,F函数是伪随机置换(PRP)或者伪随机函数(PRF)。PRP对于不同的输入有不同的输出;但是对于两个不同的输入,PRF可能产生相同的输出,即对于具有不同值的X和Y,可以有F(X)=F(Y)。但在Feistel结构...
  • 【密码学】PRP和PRF

    千次阅读 2020-11-07 20:12:11
    PRP(pseudo random permutation,伪随机置换)和PRF(pseudo random function,伪随机函数)之间的区别,可以从定义来看 PRF 取一个密钥和集合X中的元素作为输入,输出值在集合Y中,现在唯一要求的是存在一个...
  • 公钥密码学与伪随机

    千次阅读 2020-03-24 08:27:20
    公钥算法是基于数学函数而不是基于替换和置换 公钥密码是非对称的 公钥密码仅用于密钥管理和签名 关于公钥密码的误解: 从密码分析的角度看,公钥密码比传统密码更安全 公钥密码是一种通用的方法,所以传统...
  • 固定种子输入,用于产生不限长位流 伪随机函数(PRF):种子加上上下文的特定值,用于产生固定长度的伪随机串 8.1.3 对PRNG的要求 不知道种子的敌手不能决定伪随机串 对随机性、不可预测性以及种子的特性要求 1....
  • [XJTU计算机网络安全与管理]——第六讲 伪随机数,流密码,哈希
  • 一、概述 真随机数序列的定义: ①能通过所有正确的随机性检验。 ②序列的产生是不可预知的。 ③在完全相同的操作...结合真随机数的定义, 我们需要从以下几个方面重点解决伪随机数性能 的问题: ①序列能否在...
  • 伪随机函数构造安全的构造方法 令是伪随机函数,定义一个消息长度为的对称密钥加密方案如下: :输入,均匀随机地选择密钥 :输入密钥和消息,均匀随机选择,输出密文 :输入密钥和密文,输出明文 4. 伪随机函数 令...
  • 2017/3/11 9.6 random 生成伪随机数字 Python 3.6.1rc1文档 9.6random 生成伪随机数 源代码 Lib / random.py 该模块为各种分布实现伪随机数生成器 对于整数 有一个范围的均匀选择对于序列 存在随机元素的均匀选择 ...
  • 伪随机数的产生和流密码 随机数产生的原则 随机数的使用 ...确定性算法能够产生经受得住随机性检测的序列,序列并非统计随机,称之为伪随机数。 TRNG 真随机数产生器 将随机的源作为输入,称为熵源 PR
  • 伪随机函数(Pseudorandom Function)&分组密码(Block Cipher)
  • 看着是个更好点的随机数生成算法, 记录一下 以小的快速代码和小的状态大小实现了出色的统计性能, 在linear congruential generator上做出改进的 PCG在三个方面与经典线性同余生成器不同: ...
  • 依托Hadoop平台和MapReduce聚合计算能力,基于伪随机置换技术完成网络全流量并行检测,通过与数据库中的数据标签进行对比验证,来判断是否有APT攻击行为。测试结果表明,该方法可尽早从网络中发现APT异常行为,提高...
  • 密码学中PBC库的使用

    千次阅读 2021-01-26 14:32:29
    环境配置 博主虚拟机环境:Ubuntu18.04 TLS,使用PBC前需要安装gmp,这些基础的环境配置我之前写过一篇属性加密算法的可以参考。 补充一点Openssl知识,这个版本Ubuntu自带了Openssl,但是你在本地的/usr/local/lib...
  • 本文简要介绍哈希函数md2中用于随机置换的S盒数组是如何基于数学常量pi构造的。
  • 为了增强所提出的图像加密的安全性,一个多峰偏态图被用来生成用于扩散操作的伪随机灰度值序列。 通过与其他一些现有的图像加密方案进行比较,已对模拟进行了彻底的模拟。 实验结果表明,所提出的图像加密方案由于...
  • 将宿主图像的Hash值作为公钥生成伪随机序列,对原始水印信号进行扩频调制得到公开水印,用于水印的公开检测;将公开水印进行Arnold变换,产生秘密水印,用于对称检测。该算法能够实现水印的盲检测和盲提取,具有较好...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,271
精华内容 1,308
关键字:

伪随机置换

友情链接: pinyu.rar