pqc/src/poly.c

570 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2014 FH Bielefeld
*
* This file is part of a FH Bielefeld project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
/**
* @file poly.c
* This files purpose is to handle polynomials
* in general, allowing modification, arithmetic
* and common algorithms like inverting them.
* @brief operations on polynomials
*/
#include "context.h"
#include "err.h"
#include "mem.h"
#include "poly.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fmpz_poly.h>
#include <fmpz.h>
/**
* Find the inverse polynomial modulo a power of 2,
* which is q.
*
* @param a polynomial to invert
* @param Fq polynomial [out]
* @param ctx NTRU context
*/
static
void poly_mod2_to_modq(fmpz_poly_t a,
fmpz_poly_t Fq,
ntru_context *ctx);
/*------------------------------------------------------------------------*/
static void
poly_mod2_to_modq(fmpz_poly_t a,
fmpz_poly_t Fq,
ntru_context *ctx)
{
int v = 2;
fmpz_poly_t poly_tmp, two;
fmpz_poly_init(poly_tmp);
fmpz_poly_zero(poly_tmp);
fmpz_poly_init(two);
fmpz_poly_set_coeff_ui(two, 0, 2);
while (v < (int)(ctx->q)) {
v = v * 2;
poly_starmultiply(a, Fq, poly_tmp, ctx, v);
fmpz_poly_sub(poly_tmp, two, poly_tmp);
fmpz_poly_mod_unsigned(poly_tmp, v);
poly_starmultiply(Fq, poly_tmp, Fq, ctx, v);
}
fmpz_poly_clear(poly_tmp);
fmpz_poly_clear(two);
}
/*------------------------------------------------------------------------*/
void
poly_new(fmpz_poly_t new_poly,
int const * const c,
const size_t len)
{
fmpz_poly_init(new_poly);
for (uint32_t i = 0; i < len; i++)
fmpz_poly_set_coeff_si(new_poly, i, c[i]);
}
/*------------------------------------------------------------------------*/
void
poly_delete(fmpz_poly_t poly)
{
fmpz_poly_clear(poly);
}
/*------------------------------------------------------------------------*/
void
poly_delete_array(fmpz_poly_t **poly_array)
{
uint32_t i = 0;
while(poly_array[i]) {
poly_delete(*(poly_array[i]));
free(*(poly_array[i]));
i++;
}
free(poly_array);
}
/*------------------------------------------------------------------------*/
void
poly_delete_all(fmpz_poly_t poly, ...)
{
fmpz_poly_struct *next_poly;
va_list args;
next_poly = poly;
va_start(args, poly);
while (next_poly != NULL) {
poly_delete(next_poly);
next_poly = va_arg(args, fmpz_poly_struct*);
}
va_end(args);
}
/*------------------------------------------------------------------------*/
void
fmpz_poly_mod_unsigned(fmpz_poly_t a,
uint32_t mod)
{
nmod_poly_t nmod_tmp;
nmod_poly_init(nmod_tmp, mod);
fmpz_poly_get_nmod_poly(nmod_tmp, a);
fmpz_poly_set_nmod_poly_unsigned(a, nmod_tmp);
nmod_poly_clear(nmod_tmp);
}
/*------------------------------------------------------------------------*/
void
fmpz_poly_mod(fmpz_poly_t a,
uint32_t mod)
{
nmod_poly_t nmod_tmp;
nmod_poly_init(nmod_tmp, mod);
fmpz_poly_get_nmod_poly(nmod_tmp, a);
fmpz_poly_set_nmod_poly(a, nmod_tmp);
nmod_poly_clear(nmod_tmp);
}
/*------------------------------------------------------------------------*/
void
fmpz_poly_set_coeff_fmpz_n(fmpz_poly_t poly, slong n,
const fmpz_t x)
{
if (x)
fmpz_poly_set_coeff_fmpz(poly, n, x);
else
fmpz_poly_set_coeff_si(poly, n, 0);
}
/*------------------------------------------------------------------------*/
int
fmpz_invmod_ui(fmpz_t f, const fmpz_t g, uint32_t mod)
{
fmpz_t modulus;
fmpz_init_set_ui(modulus, mod);
return fmpz_invmod(f, g, modulus);
}
/*------------------------------------------------------------------------*/
void
fmpz_add_n(fmpz_t f, const fmpz_t g, const fmpz_t h)
{
if (!g && !h) {
fmpz_zero(f);
} else {
if (!g && h)
fmpz_add_ui(f, h, 0);
else if (g && !h)
fmpz_add_ui(f, g, 0);
else
fmpz_add(f, g, h);
}
}
/*------------------------------------------------------------------------*/
void
poly_starmultiply(fmpz_poly_t a,
fmpz_poly_t b,
fmpz_poly_t c,
ntru_context *ctx,
uint32_t modulus)
{
fmpz_poly_t a_tmp;
fmpz_t c_coeff_k;
fmpz_poly_init(a_tmp);
fmpz_init(c_coeff_k);
/* avoid side effects */
fmpz_poly_set(a_tmp, a);
fmpz_poly_zero(c);
for (int k = ctx->N - 1; k >= 0; k--) {
int j;
j = k + 1;
fmpz_set_si(c_coeff_k, 0);
for (int i = ctx->N - 1; i >= 0; i--) {
fmpz *a_tmp_coeff_i,
*b_coeff_j;
if (j == (int)(ctx->N))
j = 0;
a_tmp_coeff_i = fmpz_poly_get_coeff_ptr(a_tmp, i);
b_coeff_j = fmpz_poly_get_coeff_ptr(b, j);
if (a_tmp_coeff_i && fmpz_cmp_si(a_tmp_coeff_i, 0) &&
b_coeff_j && fmpz_cmp_si(b_coeff_j, 0)) {
fmpz_t fmpz_tmp;
fmpz_init(fmpz_tmp);
fmpz_mul(fmpz_tmp, a_tmp_coeff_i, b_coeff_j);
fmpz_add(fmpz_tmp, fmpz_tmp, c_coeff_k);
fmpz_mod_ui(c_coeff_k, fmpz_tmp, modulus);
fmpz_poly_set_coeff_fmpz(c, k, c_coeff_k);
fmpz_clear(fmpz_tmp);
}
j++;
}
fmpz_clear(c_coeff_k);
}
fmpz_poly_clear(a_tmp);
}
/*------------------------------------------------------------------------*/
bool
poly_inverse_poly_q(fmpz_poly_t a,
fmpz_poly_t Fq,
ntru_context *ctx)
{
bool retval = false;
int k = 0,
j = 0;
fmpz *b_last;
fmpz_poly_t a_tmp,
b,
c,
f,
g;
/* general initialization of temp variables */
fmpz_poly_init(b);
fmpz_poly_set_coeff_ui(b, 0, 1);
fmpz_poly_init(c);
fmpz_poly_init(f);
fmpz_poly_set(f, a);
/* set g(x) = x^N 1 */
fmpz_poly_init(g);
fmpz_poly_set_coeff_si(g, 0, -1);
fmpz_poly_set_coeff_si(g, ctx->N, 1);
/* avoid side effects */
fmpz_poly_init(a_tmp);
fmpz_poly_set(a_tmp, a);
fmpz_poly_zero(Fq);
while (1) {
while (fmpz_is_zero(fmpz_poly_get_coeff_ptr(f, 0))) {
for (uint32_t i = 1; i <= ctx->N; i++) {
fmpz *f_coeff = fmpz_poly_get_coeff_ptr(f, i);
fmpz *c_coeff = fmpz_poly_get_coeff_ptr(c, ctx->N - i);
/* f(x) = f(x) / x */
fmpz_poly_set_coeff_fmpz_n(f, i - 1,
f_coeff);
/* c(x) = c(x) * x */
fmpz_poly_set_coeff_fmpz_n(c, ctx->N + 1 - i,
c_coeff);
}
fmpz_poly_set_coeff_si(f, ctx->N, 0);
fmpz_poly_set_coeff_si(c, 0, 0);
k++;
if (fmpz_poly_degree(f) == -1)
goto _cleanup;
}
if (fmpz_poly_degree(f) == 0)
break;
if (fmpz_poly_degree(f) < fmpz_poly_degree(g)) {
fmpz_poly_swap(f, g);
fmpz_poly_swap(b, c);
}
fmpz_poly_add(f, g, f);
fmpz_poly_mod_unsigned(f, 2);
fmpz_poly_add(b, c, b);
fmpz_poly_mod_unsigned(b, 2);
}
k = k % ctx->N;
b_last = fmpz_poly_get_coeff_ptr(b, ctx->N);
if (b_last && fmpz_cmp_si(b_last, 0))
goto _cleanup;
/* Fq(x) = x^(N-k) * b(x) */
for (int i = ctx->N - 1; i >= 0; i--) {
fmpz *b_i;
j = i - k;
if (j < 0)
j = j + ctx->N;
b_i = fmpz_poly_get_coeff_ptr(b, i);
fmpz_poly_set_coeff_fmpz_n(Fq, j, b_i);
}
poly_mod2_to_modq(a_tmp, Fq, ctx);
/* check if the f * Fq = 1 (mod p) condition holds true */
fmpz_poly_set(a_tmp, a);
poly_starmultiply(a_tmp, Fq, a_tmp, ctx, ctx->q);
if (fmpz_poly_is_one(a_tmp))
retval = true;
else
fmpz_poly_zero(Fq);
_cleanup:
fmpz_poly_clear(a_tmp);
fmpz_poly_clear(b);
fmpz_poly_clear(c);
fmpz_poly_clear(f);
fmpz_poly_clear(g);
return retval;
}
/*------------------------------------------------------------------------*/
bool
poly_inverse_poly_p(fmpz_poly_t a,
fmpz_poly_t Fp,
ntru_context *ctx)
{
bool retval = false;
int k = 0,
j = 0;
fmpz *b_last;
fmpz_poly_t a_tmp,
b,
c,
f,
g;
/* general initialization of temp variables */
fmpz_poly_init(b);
fmpz_poly_set_coeff_ui(b, 0, 1);
fmpz_poly_init(c);
fmpz_poly_init(f);
fmpz_poly_set(f, a);
/* set g(x) = x^N 1 */
fmpz_poly_init(g);
fmpz_poly_set_coeff_si(g, 0, -1);
fmpz_poly_set_coeff_si(g, ctx->N, 1);
/* avoid side effects */
fmpz_poly_init(a_tmp);
fmpz_poly_set(a_tmp, a);
fmpz_poly_zero(Fp);
while (1) {
while (fmpz_is_zero(fmpz_poly_get_coeff_ptr(f, 0))) {
for (uint32_t i = 1; i <= ctx->N; i++) {
fmpz *f_coeff = fmpz_poly_get_coeff_ptr(f, i);
fmpz *c_coeff = fmpz_poly_get_coeff_ptr(c, ctx->N - i);
/* f(x) = f(x) / x */
fmpz_poly_set_coeff_fmpz_n(f, i - 1,
f_coeff);
/* c(x) = c(x) * x */
fmpz_poly_set_coeff_fmpz_n(c, ctx->N + 1 - i,
c_coeff);
}
fmpz_poly_set_coeff_si(f, ctx->N, 0);
fmpz_poly_set_coeff_si(c, 0, 0);
k++;
if (fmpz_poly_degree(f) == -1)
goto cleanup;
}
if (fmpz_poly_degree(f) == 0)
break;
if (fmpz_poly_degree(f) < fmpz_poly_degree(g)) {
/* exchange f and g and exchange b and c */
fmpz_poly_swap(f, g);
fmpz_poly_swap(b, c);
}
{
fmpz_poly_t c_tmp,
g_tmp;
fmpz_t u,
mp_tmp;
fmpz_init(u);
fmpz_zero(u);
fmpz_init_set(mp_tmp, fmpz_poly_get_coeff_ptr(f, 0));
fmpz_poly_init(g_tmp);
fmpz_poly_set(g_tmp, g);
fmpz_poly_init(c_tmp);
fmpz_poly_set(c_tmp, c);
/* u = f[0] * g[0]^(-1) mod p */
/* = (f[0] mod p) * (g[0] inverse mod p) mod p */
fmpz_invmod_ui(u,
fmpz_poly_get_coeff_ptr(g, 0),
ctx->p);
fmpz_mod_ui(mp_tmp, mp_tmp, ctx->p);
fmpz_mul(u, mp_tmp, u);
fmpz_mod_ui(u, u, ctx->p);
/* f = f - u * g mod p */
fmpz_poly_scalar_mul_fmpz(g_tmp, g_tmp, u);
fmpz_poly_sub(f, g_tmp, f);
fmpz_poly_mod_unsigned(f, ctx->p);
/* b = b - u * c mod p */
fmpz_poly_scalar_mul_fmpz(c_tmp, c_tmp, u);
fmpz_poly_sub(b, c_tmp, b);
fmpz_poly_mod_unsigned(b, ctx->p);
fmpz_clear(u);
fmpz_poly_clear(g_tmp);
fmpz_poly_clear(c_tmp);
}
}
k = k % ctx->N;
b_last = fmpz_poly_get_coeff_ptr(b, ctx->N);
if (b_last && fmpz_cmp_si(b_last, 0))
goto cleanup;
/* Fp(x) = x^(N-k) * b(x) */
for (int i = ctx->N - 1; i >= 0; i--) {
fmpz *b_i;
/* b(X) = f[0]^(-1) * b(X) (mod p) */
{
fmpz_t mp_tmp;
fmpz_init(mp_tmp);
fmpz_invmod_ui(mp_tmp,
fmpz_poly_get_coeff_ptr(f, 0),
ctx->p);
if (fmpz_poly_get_coeff_ptr(b, i)) {
fmpz_mul(fmpz_poly_get_coeff_ptr(b, i),
fmpz_poly_get_coeff_ptr(b, i),
mp_tmp);
fmpz_mod_ui(fmpz_poly_get_coeff_ptr(b, i),
fmpz_poly_get_coeff_ptr(b, i),
ctx->p);
}
}
j = i - k;
if (j < 0)
j = j + ctx->N;
b_i = fmpz_poly_get_coeff_ptr(b, i);
fmpz_poly_set_coeff_fmpz_n(Fp, j, b_i);
}
/* check if the f * Fp = 1 (mod p) condition holds true */
fmpz_poly_set(a_tmp, a);
poly_starmultiply(a_tmp, Fp, a_tmp, ctx, ctx->p);
if (fmpz_poly_is_one(a_tmp))
retval = true;
else
fmpz_poly_zero(Fp);
cleanup:
fmpz_poly_clear(a_tmp);
fmpz_poly_clear(b);
fmpz_poly_clear(c);
fmpz_poly_clear(f);
fmpz_poly_clear(g);
return retval;
}
/*------------------------------------------------------------------------*/
void
poly_draw(fmpz_poly_t poly)
{
fmpz_poly_print(poly);
flint_printf("\n");
}
/*------------------------------------------------------------------------*/
void
poly_draw_pretty(fmpz_poly_t poly)
{
fmpz_poly_print_pretty(poly, "x");
flint_printf("\n");
}
/*------------------------------------------------------------------------*/