Files

248 lines
7.2 KiB
C

/* This file is for implementing the Nassimi-Sahni algorithm */
/* See David Nassimi, Sartaj Sahni "Parallel algorithms to set up the Benes permutationnetwork" */
/* See also https://cr.yp.to/papers/controlbits-20200923.pdf */
#include "compat.h"
#include "controlbits.h"
#include "crypto_declassify.h"
#include "int32_sort.h"
#include <string.h>
typedef int16_t int16;
typedef int32_t int32;
#include "crypto_int32.h"
#define int32_min crypto_int32_min
#include "crypto_int16.h"
/* parameters: 1 <= w <= 14; n = 2^w */
/* input: permutation pi of {0,1,...,n-1} */
/* output: (2m-1)n/2 control bits at positions pos,pos+step,... */
/* output position pos is by definition 1&(out[pos/8]>>(pos&7)) */
/* caller must 0-initialize positions first */
/* temp must have space for int32[2*n] */
static void cbrecursion(unsigned char *out, long long pos, long long step, const int16 *pi, long long w, long long n, int32 *temp) {
#define A temp
#define B (temp+n)
#define q ((int16 *) (temp+n+n/4))
/* q can start anywhere between temp+n and temp+n/2 */
long long x, y, i, j, k;
if (w == 1) {
out[pos >> 3] ^= pi[0] << (pos & 7);
return;
}
for (x = 0; x < n; ++x) {
A[x] = ((pi[x] ^ 1) << 16) | pi[x ^ 1];
}
int32_sort(A, n); /* A = (id<<16)+pibar */
for (x = 0; x < n; ++x) {
int32 Ax = A[x];
int32 px = Ax & 0xffff;
int32 cx = int32_min(px, (int32)x);
B[x] = ((px) << 16) | cx;
}
/* B = (p<<16)+c */
for (x = 0; x < n; ++x) {
A[x] = (int32)((((uint32_t)A[x]) << 16) | x); /* A = (pibar<<16)+id */
}
int32_sort(A, n); /* A = (id<<16)+pibar^-1 */
for (x = 0; x < n; ++x) {
A[x] = (((uint32_t)A[x]) << 16) + (B[x] >> 16); /* A = (pibar^(-1)<<16)+pibar */
}
int32_sort(A, n); /* A = (id<<16)+pibar^2 */
if (w <= 10) {
for (x = 0; x < n; ++x) {
B[x] = ((A[x] & 0xffff) << 10) | (B[x] & 0x3ff);
}
for (i = 1; i < w - 1; ++i) {
/* B = (p<<10)+c */
for (x = 0; x < n; ++x) {
A[x] = (int32)(((B[x] & ~0x3ff) << 6) | x); /* A = (p<<16)+id */
}
int32_sort(A, n); /* A = (id<<16)+p^{-1} */
for (x = 0; x < n; ++x) {
A[x] = ((uint32_t)A[x] << 20) | B[x];
} /* A = (p^{-1}<<20)+(p<<10)+c */
int32_sort(A, n); /* A = (id<<20)+(pp<<10)+cp */
for (x = 0; x < n; ++x) {
int32 ppcpx = A[x] & 0xfffff;
int32 ppcx = (A[x] & 0xffc00) | (B[x] & 0x3ff);
B[x] = int32_min(ppcx, ppcpx);
}
}
for (x = 0; x < n; ++x) {
B[x] &= 0x3ff;
}
} else {
for (x = 0; x < n; ++x) {
B[x] = (((uint32_t)A[x]) << 16) | (B[x] & 0xffff);
}
for (i = 1; i < w - 1; ++i) {
/* B = (p<<16)+c */
for (x = 0; x < n; ++x) {
A[x] = (int32)((B[x] & ~0xffff) | x);
}
int32_sort(A, n); /* A = (id<<16)+p^(-1) */
for (x = 0; x < n; ++x) {
A[x] = (((uint32_t)A[x]) << 16) | (B[x] & 0xffff);
}
/* A = p^(-1)<<16+c */
if (i < w - 2) {
for (x = 0; x < n; ++x) {
B[x] = (A[x] & ~0xffff) | (B[x] >> 16);
}
/* B = (p^(-1)<<16)+p */
int32_sort(B, n); /* B = (id<<16)+p^(-2) */
for (x = 0; x < n; ++x) {
B[x] = (((uint32_t)B[x]) << 16) | (A[x] & 0xffff);
}
/* B = (p^(-2)<<16)+c */
}
int32_sort(A, n);
/* A = id<<16+cp */
for (x = 0; x < n; ++x) {
int32 cpx = (B[x] & ~0xffff) | (A[x] & 0xffff);
B[x] = int32_min(B[x], cpx);
}
}
for (x = 0; x < n; ++x) {
B[x] &= 0xffff;
}
}
for (x = 0; x < n; ++x) {
A[x] = (int32)((((int32)pi[x]) << 16) + x);
}
int32_sort(A, n); /* A = (id<<16)+pi^(-1) */
for (j = 0; j < n / 2; ++j) {
x = 2 * j;
int32 fj = B[x] & 1; /* f[j] */
int32 Fx = (int32)(x + fj); /* F[x] */
int32 Fx1 = Fx ^ 1; /* F[x+1] */
out[pos >> 3] ^= fj << (pos & 7);
pos += step;
B[x] = ((uint32_t)A[x] << 16) | Fx;
B[x + 1] = ((uint32_t)A[x + 1] << 16) | Fx1;
}
/* B = (pi^(-1)<<16)+F */
int32_sort(B, n); /* B = (id<<16)+F(pi) */
pos += (2 * w - 3) * step * (n / 2);
for (k = 0; k < n / 2; ++k) {
y = 2 * k;
int32 lk = B[y] & 1; /* l[k] */
int32 Ly = (int32)(y + lk); /* L[y] */
int32 Ly1 = Ly ^ 1; /* L[y+1] */
out[pos >> 3] ^= lk << (pos & 7);
pos += step;
A[y] = (Ly << 16) | (B[y] & 0xffff);
A[y + 1] = (Ly1 << 16) | (B[y + 1] & 0xffff);
}
/* A = (L<<16)+F(pi) */
int32_sort(A, n); /* A = (id<<16)+F(pi(L)) = (id<<16)+M */
pos -= (2 * w - 2) * step * (n / 2);
for (j = 0; j < n / 2; ++j) {
q[j] = (A[2 * j] & 0xffff) >> 1;
q[j + n / 2] = (A[2 * j + 1] & 0xffff) >> 1;
}
cbrecursion(out, pos, step * 2, q, w - 1, n / 2, temp);
cbrecursion(out, pos + step, step * 2, q + n / 2, w - 1, n / 2, temp);
}
/* input: p, an array of int16 */
/* input: n, length of p */
/* input: s, meaning that stride-2^s cswaps are performed */
/* input: cb, the control bits */
/* output: the result of apply the control bits to p */
static void layer(int16_t *p, const unsigned char *cb, int s, int n) {
int i, j;
int stride = 1 << (uint8_t)(s & 0x1F);
int index = 0;
int16_t d, m;
for (i = 0; i < n; i += stride * 2) {
for (j = 0; j < stride; j++) {
d = p[ i + j ] ^ p[ i + j + stride ];
m = (cb[ index >> 3 ] >> (index & 7)) & 1;
m = -m;
d &= m;
p[ i + j ] ^= d;
p[ i + j + stride ] ^= d;
index++;
}
}
}
/* parameters: 1 <= w <= 14; n = 2^w */
/* input: permutation pi of {0,1,...,n-1} */
/* output: (2m-1)n/2 control bits at positions 0,1,... */
/* output position pos is by definition 1&(out[pos/8]>>(pos&7)) */
void controlbitsfrompermutation(unsigned char *out, const int16 *pi, long long w, long long n) {
PQCLEAN_VLA(int32, temp, (size_t)(2 * n));
PQCLEAN_VLA(int16, pi_test, (size_t)n);
int16 diff;
int i;
unsigned char *ptr;
memset(temp, 0, (size_t)n * 2);
memset(pi_test, 0, (size_t)n);
while (1) {
memset(out, 0, (size_t)((((2 * w - 1)*n / 2) + 7) / 8));
cbrecursion(out, 0, 1, pi, w, n, temp);
// check for correctness
for (i = 0; i < n; i++) {
pi_test[i] = (int16)i;
}
ptr = out;
for (i = 0; i < w; i++) {
layer(pi_test, ptr, i, (int32)n);
ptr += n >> 4;
}
for (i = (int)(w - 2); i >= 0; i--) {
layer(pi_test, ptr, i, (int32)n);
ptr += n >> 4;
}
diff = 0;
for (i = 0; i < n; i++) {
diff |= pi[i] ^ pi_test[i];
}
diff = crypto_int16_nonzero_mask(diff);
crypto_declassify(&diff, sizeof diff);
if (diff == 0) {
break;
}
}
}