/* Author: Pate Williams (c) 1997 Berlekamp's Q-matrix algorithm. See "Handbook of Applied Cryptography" by Alfred J. Menezes et al 3.11.2 Section 3.111 Algorithm page 124. Also see "Seminumerical Algorithms" by Donald E. Knuth second edition page 423 and "A Course in Computational Algebraic Number Theory" by Henri Cohen Algorithm 3.4.10 page 132. */ #include #include #include #include "lip.h" #define DEBUG #define POLY_SIZE 8192l verylong **create_matrix(long m, long n) { long i; verylong **matrix = calloc(m, sizeof(verylong)); assert(matrix != 0); for (i = 0; i < m; i++) { matrix[i] = calloc(n, sizeof(verylong)); assert(matrix[i] != 0); } return matrix; } void delete_matrix(long m, verylong **matrix) { long i; for (i = 0; i < m; i++) free(matrix[i]); free(matrix); } void zpoly_mul(long m, long n, verylong *za, verylong *zb, verylong *zc, long *p) { long i, j, k; verylong zd = 0, zai = 0, zbk = 0, zsum = 0, zterm = 0; *p = m + n; for (k = 0; k <= *p; k++) { zzero(&zsum); for (i = 0; i <= k; i++) { j = k - i; if (i > m) zzero(&zai); else zcopy(za[i], &zai); if (j > n) zzero(&zbk); else zcopy(zb[j], &zbk); zmul(zai, zbk, &zterm); zcopy(zsum, &zd); zadd(zterm, zd, &zsum); } zcopy(zsum, &zc[k]); } zfree(&zd); zfree(&zai); zfree(&zbk); zfree(&zsum); zfree(&zterm); } void zpoly_div(long m, long n, verylong *zu, verylong *zv, verylong *zq, verylong *zr, long *p, long *s) { long j, jk, k, nk; verylong za = 0, zb = 0, zvn = 0; zcopy(zv[n], &zvn); for (j = 0; j <= m; j++) zcopy(zu[j], &zr[j]); if (m < n) { *p = 0, *s = m; zzero(&zq[0]); } else { *p = m - n, *s = n - 1; for (k = *p; k >= 0; k--) { nk = n + k; zsexp(zvn, k, &za); zmul(zr[nk], za, &zq[k]); for (j = nk - 1; j >= 0; j--) { jk = j - k; if (jk >= 0) { zmul(zvn, zr[j], &za); zmul(zr[nk], zv[jk], &zb); zsub(za, zb, &zr[j]); } else { zcopy(zr[j], &za); zmul(zvn, za, &zr[j]); } } } while (*p > 0 && zscompare(zq[*p], 0l) == 0) *p = *p - 1; while (*s > 0 && zscompare(zr[*s], 0l) == 0) *s = *s - 1; } zfree(&za); zfree(&zb); zfree(&zvn); } void zpoly_mod(long p, verylong *za, long *da) { long i; for (i = 0; i <= *da; i++) zintoz(zsmod(za[i], p), &za[i]); while (*da > 0 && zscompare(za[*da], 0l) == 0) *da = *da - 1; } void zpoly_pow(long degreeA, long degreem, long p, verylong zn, verylong *zA, verylong *zm, verylong *zs, long *ds) { long dp, dq, dx = degreeA, i; verylong za = 0, zb = 0, zp[POLY_SIZE], zq[POLY_SIZE], zx[POLY_SIZE], zy[POLY_SIZE]; for (i = 0; i < POLY_SIZE; i++) zp[i] = zq[i] = zx[i] = zy[i] = 0; *ds = 0; zcopy(zn, &za); zone(&zs[0]); for (i = 0; i <= dx; i++) zcopy(zA[i], &zx[i]); while (zscompare(za, 0l) > 0) { if (zodd(za)) { /* s = (s * x) % m; */ zpoly_mul(*ds, dx, zs, zx, zp, &dp); zpoly_div(dp, degreem, zp, zm, zq, zs, &dq, ds); zpoly_mod(p, zs, ds); } zcopy(za, &zb); zrshift(zb, 1l, &za); if (zscompare(za, 0l) > 0) { /* x = (x * x) % m; */ for (i = 0; i <= dx; i++) zcopy(zx[i], &zy[i]); zpoly_mul(dx, dx, zx, zy, zp, &dp); zpoly_div(dp, degreem, zp, zm, zq, zx, &dq, &dx); zpoly_mod(p, zx, &dx); } } zfree(&za); zfree(&zb); for (i = 0; i < POLY_SIZE; i++) { zfree(&zp[i]); zfree(&zq[i]); zfree(&zx[i]); zfree(&zy[i]); } } void zpoly_sub(long da, long db, verylong *za, verylong *zb, verylong *zc, long *dc) { long i; verylong zz = 0; zzero(&zz); if (da >= db) { for (i = 0; i <= db; i++) zsub(za[i], zb[i], &zc[i]); for (i = db + 1; i <= da; i++) zcopy(za[i], &zc[i]); *dc = da; } else { for (i = 0; i <= da; i++) zsub(za[i], zb[i], &zc[i]); for (i = da + 1; i <= db; i++) zsub(zz, zb[i], &zc[i]); *dc = db; } zfree(&zz); } void zpoly_gcd(long degreeA, long degreeB, long p, verylong *zA, verylong *zB, verylong *za, long *da) { int nonzero = 0, zero; long db, dq, dr, i; verylong zc = 0, zd = 0, zp = 0; verylong zb[POLY_SIZE], zq[POLY_SIZE], zr[POLY_SIZE]; for (i = 0; i < POLY_SIZE; i++) zb[i] = zq[i] = zr[i] = 0; if (degreeA > degreeB) { *da = degreeA; db = degreeB; for (i = 0; i <= *da; i++) zcopy(zA[i], &za[i]); for (i = 0; i <= db; i++) zcopy(zB[i], &zb[i]); } else { *da = degreeB; db = degreeA; for (i = 0; i <= *da; i++) zcopy(zB[i], &za[i]); for (i = 0; i <= db; i++) zcopy(zA[i], &zb[i]); } for (i = 0; i <= db && !nonzero; i++) nonzero = zscompare(zb[i], 0l) != 0; zintoz(p, &zp); while (nonzero) { zpoly_div(*da, db, za, zb, zq, zr, &dq, &dr); for (i = 0; i <= dr; i++) { zcopy(zr[i], &zc); zmod(zc, zp, &zr[i]); } zero = 1; for (i = dr; i >= 0 && zero; i--) { zero = zscompare(zr[i], 0l) == 0; if (zero && dr > 0) dr--; } for (i = 0; i <= db; i++) zcopy(zb[i], &za[i]); *da = db; for (i = 0; i <= dr; i++) zcopy(zr[i], &zb[i]); db = dr; nonzero = 0; for (i = 0; i <= db && !nonzero; i++) nonzero = zscompare(zb[i], 0l) != 0; } if (*da > 0) { zinvmod(za[*da], zp, &zd); for (i = 0; i <= *da; i++) { zmulmod(za[i], zd, zp, &zc); zcopy(zc, &za[i]); } } zfree(&zc); zfree(&zd); zfree(&zp); for (i = 0; i < POLY_SIZE; i++) { zfree(&zb[i]); zfree(&zq[i]); zfree(&zr[i]); } } void zpoly_copy(long da, verylong *za, verylong *zb, long *db) { long i; *db = da; for (i = 0; i <= da; i++) zcopy(za[i], &zb[i]); } void zpoly_print(long da, verylong *za) { long i; for (i = da; i >= 0; i--) { zwrite(za[i]); printf(" "); } printf("\n"); } void zpoly_ext_euclid(long dg, long dh, long p, verylong *zg, verylong *zh, verylong *zs, verylong *zt, verylong *zd, long *ds, long *dt, long *dd) { long da, dq, dr, ds1 = 0, ds2 = 0, dt1 = 0, dt2 = 0, i; verylong za[POLY_SIZE], zb[POLY_SIZE]; verylong zq[POLY_SIZE], zr[POLY_SIZE]; verylong zs1[POLY_SIZE], zs2[POLY_SIZE]; verylong zt1[POLY_SIZE], zt2[POLY_SIZE]; if (dh == 0 && zscompare(zh[0], 0l) == 0) { zpoly_copy(dg, zg, zd, dd); *ds = *dt = 0; zone(&zs[0]); zzero(&zt[0]); } for (i = 0; i < POLY_SIZE; i++) { za[i] = zb[i] = zq[i] = zr[i] = 0; zs1[i] = zs2[i] = zt1[i] = zt2[i] = 0; } zone(&zs2[0]); zzero(&zs1[0]); zzero(&zt2[0]); zone(&zt1[0]); while (dh != 0 || zscompare(zh[0], 0l) != 0) { zpoly_div(dg, dh, zg, zh, zq, zr, &dq, &dr); zpoly_mod(p, zq, &dq); zpoly_mod(p, zr, &dr); zpoly_mul(dq, ds1, zq, zs1, za, &da); zpoly_sub(ds2, da, zs2, za, zs, ds); zpoly_mul(dq, dt1, zq, zt1, za, &da); zpoly_sub(dt2, da, zt2, za, zt, dt); zpoly_mod(p, zs, ds); zpoly_mod(p, zt, dt); zpoly_copy(dh, zh, zg, &dg); zpoly_copy(dr, zr, zh, &dh); zpoly_copy(ds1, zs1, zs2, &ds2); zpoly_copy(*ds, zs, zs1, &ds1); zpoly_copy(dt1, zt1, zt2, &dt2); zpoly_copy(*dt, zt, zt1, &dt1); #ifdef DEBUG printf("q = "); zpoly_print(dq, zq); printf("r = "); zpoly_print(dr, zr); printf("s = "); zpoly_print(*ds, zs); printf("t = "); zpoly_print(*dt, zt); printf("g = "); zpoly_print(dg, zg); printf("h = "); zpoly_print(dh, zh); printf("s2 = "); zpoly_print(ds2, zs2); printf("s1 = "); zpoly_print(ds1, zs1); printf("t2 = "); zpoly_print(dt2, zt2); printf("t1 = "); zpoly_print(dt1, zt1); #endif } zpoly_copy(dg, zg, zd, dd); zpoly_copy(ds2, zs2, zs, ds); zpoly_copy(dt2, zt2, zt, dt); for (i = 0; i < POLY_SIZE; i++) { zfree(&za[i]); zfree(&zb[i]); zfree(&zq[i]); zfree(&zr[i]); zfree(&zs1[i]); zfree(&zs2[i]); zfree(&zt1[i]); zfree(&zt2[i]); } } void kernel(long m, long n, long p, verylong **zM, verylong **zX, long *r) { int found; long *c = calloc(m, sizeof(long)), i, j, k, s; verylong zD = 0, za = 0, zb = 0, zp = 0; assert(c != 0); zintoz(p, &zp); *r = 0; for (i = 0; i < m; i++) c[i] = - 1; for (k = 0; k < m; k++) { found = 0, j = 0; while (!found && j < m) { found = zscompare(zM[k][j], 0l) != 0 && c[j] < 0; if (!found) j++; } if (found) { zinvmod(zM[k][j], zp, &zD); znegate(&zD); zintoz(- 1l, &zM[k][j]); for (s = k + 1; s < m; s++) { zmul(zD, zM[s][j], &za); zmod(za, zp, &zM[s][j]); } for (i = 0; i < n; i++) { if (i != j) { zcopy(zM[k][i], &zD); zzero(&zM[k][i]); for (s = k + 1; s < m; s++) { zmul(zD, zM[s][j], &za); zadd(zM[s][i], za, &zb); zmod(zb, zp, &zM[s][i]); } } } c[j] = k; } else { for (j = 0; j < n; j++) zzero(&zX[*r][j]); for (j = 0; j < n; j++) { for (s = 0; s < n; s++) { if (j == k) zone(&zX[*r][j]); else if (c[s] == j) zcopy(zM[k][s], &zX[*r][j]); } } *r = *r + 1; } } free(c); zfree(&zD); zfree(&za); zfree(&zb); zfree(&zp); } void Berlekamp_algorithm(long n, long p, verylong *zu, verylong **zc, long *count) { long da, db, dw, i, index = 0, j, k, s, r; verylong zd = 0, zn = 0, zp = 0; verylong za[POLY_SIZE], zb[POLY_SIZE]; verylong zw[POLY_SIZE], zX[POLY_SIZE]; verylong **zq = create_matrix(n, n), **zv = create_matrix(n, n); for (i = 0; i < POLY_SIZE; i++) za[i] = zb[i] = zw[i] = zX[i] = 0; zone(&zq[0][0]); zzero(&zX[0]); zone(&zX[1]); for (k = 1; k < n; k++) zzero(&zq[0][k]); zintoz(p, &zp); for (k = 1; k < n; k++) { zsmul(zp, k, &zn); zpoly_pow(1l, n, p, zn, zX, zu, za, &da); for (j = 0; j < n; j++) zcopy(za[j], &zq[k][j]); } #ifdef DEBUG printf("the Q-matrix is as follows:\n\n"); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { zwrite(zq[i][j]); printf(" "); } printf("\n"); } printf("\n"); #endif for (i = 0; i < n; i++) { zsadd(zq[i][i], - 1l, &zd); zcopy(zd, &zq[i][i]); } kernel(n, n, p, zq, zv, &r); #ifdef DEBUG printf("the linearly independent vectors are as follows:\n\n"); for (i = 0; i < r; i++) { for (j = 0; j < n; j++) { zwrite(zv[i][j]); printf(" "); } printf("\n"); } printf("\n"); #endif for (i = 0; i <= n; i++) zcopy(zu[i], &zw[i]); dw = n; *count = 0; for (i = 1; i < r; i++) { for (s = 0; s < p; s++) { for (j = 0; j < n; j++) zcopy(zv[i][j], &za[j]); zsadd(za[0], - s, &zd); zcopy(zd, &za[0]); da = n - 1; while (da > 0 && zscompare(za[da], 0l) == 0) da--; zpoly_gcd(da, dw, p, za, zw, zb, &db); if (db != 0) { for (j = 0; j <= db; j++) zcopy(zb[j], &zc[*count][j]); *count = *count + 1; } } for (j = 0; j < n; j++) zcopy(zc[index][j], &zw[j]); index++; dw = n - 1, j = dw; while (dw > 0) if (zscompare(zw[j--], 0l) == 0) dw--; else break; } delete_matrix(n, zq); delete_matrix(n, zv); zfree(&zd); zfree(&zn); zfree(&zp); for (i = 0; i < POLY_SIZE; i++) { zfree(&za[i]); zfree(&zb[i]); zfree(&zw[i]); zfree(&zX[i]); } } int main(void) { long i, j, n = 8, p = 13l, r; verylong zu[POLY_SIZE]; verylong **zX = create_matrix(8, 8); for (i = 0; i < POLY_SIZE; i++) zu[i] = 0; zone(&zu[8]); zone(&zu[6]); zintoz(10l, &zu[4]); zintoz(10l, &zu[3]); zintoz(8l, &zu[2]); zintoz(2l, &zu[1]); zintoz(8l, &zu[0]); printf("u = "); zpoly_print(n, zu); printf("\n"); Berlekamp_algorithm(n, p, zu, zX, &r); printf("the linear independent polynomials are as follows:\n\n"); for (i = 0; i < r; i++) { for (j = 0; j < n; j++) { zwrite(zX[i][j]); printf(" "); } printf("\n"); } delete_matrix(8, zX); for (i = 0; i < POLY_SIZE; i++) zfree(&zu[i]); return 0; }