-rw-r--r-- 3622 high-ctidh-20210523/random.c
#include <assert.h> #include "random.h" #include "randombytes.h" #include "crypto_declassify.h" #include "int32_sort.h" #include "int32mask.h" void random_boundedl1(int8_t *e,const long long w,const long long S) { // standard correspondences: // e[0],e[1],...,e[w-1] are w nonnegative integers // with sum at most S // <=> 1+e[0],1+e[1],...,1+e[w-1] are w positive integers // with sum at most S+w // <=> 1+e[0],1+e[0]+1+e[1],... are w integers // in strictly increasing order // between 1 and S+w // <=> e[0],1+e[0]+e[1],... are w integers // in strictly increasing order // between 0 and S+w-1 assert(w >= 0); assert(w < 128); assert(S >= 0); assert(S < 128); if (!w) return; const long long rnum = S+w; assert(rnum <= 254); /* XXX: Microsoft compiler doesn't handle int32_t r[S+w] */ int32_t r[254]; for (;;) { /* rejection-sampling loop */ randombytes(r,4*rnum); for (long long j = 0;j < rnum;++j) r[j] &= ~1; for (long long j = 0;j < w;++j) r[j] |= 1; int32_sort(r,rnum); long long collision = 0; for (long long j = 1;j < w;++j) collision |= int32mask_zero((r[j]^r[j-1])&~1); crypto_declassify(&collision,sizeof collision); if (collision) continue; for (long long j = 0;j < rnum;++j) r[j] &= 1; // now r has Hamming weight w for (long long j = 1;j < rnum;++j) r[j] += r[j-1]; // now r has >=0 copies of 0, // >=1 copies of 1, >=1 copies of 2, ..., >=1 copies of w for (long long i = 0;i < w;++i) { long long numi = 0; for (long long j = 0;j < rnum;++j) numi -= int32mask_equal(r[j],i); e[i] = numi; } // now e[0]>=0, e[1]>=1, e[2]>=1, ..., e[w-1]>=1 // and ghost e[w]>=1, not stored // e[0]+e[1]+...+e[w] = rnum // so e[0]+e[1]+...+e[w-1] <= rnum-1 for (long long i = 1;i < w;++i) --e[i]; // now e[0]>=0, e[1]>=0, e[2]>=0, ..., e[w-1]>=0 // sum <= (rnum-1)-(w-1) // i.e. sum <= S // not done yet: want to allow negative e // but simply negating will give zeros too much weight // for uniformity, keep e with probability 1/2^z // where z is the number of zeros in e // speedup: actually keep e with probability 2^zmin/2^z // where zmin is minimum possible value of z // strategy: start counter at w-S // and decrease by 1 for each zero bit // and, if negative, flip coin for each zero bit long long counter = w-S; randombytes(r,4*((w+31)/32)); long long reject = 0; for (long long i = 0;i < w;++i) { int32_t rbit = 1&(r[i/32]>>(i&31)); int32_t eizeromask = int32mask_zero(e[i]); counter += eizeromask; reject |= int32mask_negative(counter)&eizeromask&rbit; } crypto_declassify(&reject,sizeof reject); if (reject) continue; // ok to reuse randomness in r: // bits used here are for e[i] nonzero, // bits used above are for e[i] zero for (long long i = 0;i < w;++i) { int32_t rbit = 1&(r[i/32]>>(i&31)); e[i] ^= -rbit; e[i] += rbit; } return; } } // -1 if x < y, else 0 static int64_t uint64mask_lessthan(uint64_t x,uint64_t y) { int64_t xy = x^y; int64_t c = x-y; const int64_t flip = ((int64_t) 1)<<63; c ^= xy&(c^x^flip); return c>>63; } int64_t random_coin(uint64_t num,uint64_t den) { uint8_t buf[32]; uint64_t r = 0; randombytes(buf,sizeof buf); for (long long i = 0;i < 256;++i) { uint64_t bit = 1&(buf[i/8]>>(i&7)); r <<= 1; r += bit; r ^= (~uint64mask_lessthan(r,den))&(r^(r-den)); } // XXX: speed this up return uint64mask_lessthan(r,num); }