1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
/*
* SPDX-License-Identifier: CC0-1.0
*
* Copyright (C) 2024, 2025 W. Kosior <koszko@koszko.org>
*/
#include <stdbool.h>
#include <stdlib.h>
#include <sys/types.h>
#include <flint/flint.h>
#include <flint/fmpz_mod.h>
#include "pqcrypto_poly.h"
#include "pqcrypto_prng_getrandom.h"
/***
*** Modulo operations.
***/
void mod_c0_ctx_init(mod_centered_0_ctx_t ctx, fmpz_t divisor) {
fmpz_init_set(ctx[0].divisor, divisor);
fmpz_init(ctx[0].range_max);
fmpz_sub_ui(ctx[0].range_max, ctx[0].divisor, 1);
fmpz_tdiv_q_2exp(ctx[0].range_max, ctx[0].range_max, 1);
}
void mod_c0_ctx_clear(mod_centered_0_ctx_t ctx) {
fmpz_clear(ctx[0].divisor);
fmpz_clear(ctx[0].range_max);
}
void mod_c0(fmpz_t value, mod_centered_0_ctx_t const ctx) {
fmpz_add(value, value, ctx[0].range_max);
fmpz_fdiv_r(value, value, ctx[0].divisor);
fmpz_sub(value, value, ctx[0].range_max);
}
void mod_c0_ctx_init_set(mod_centered_0_ctx_t dst_ctx,
mod_centered_0_ctx_t const src_ctx) {
fmpz_init_set(dst_ctx[0].divisor, src_ctx[0].divisor);
fmpz_init_set(dst_ctx[0].range_max, src_ctx[0].range_max);
}
void mod_c0_prng(fmpz_t value, mod_centered_0_ctx_t const ctx,
prng_t prng, void * prng_state) {
flint_bitcnt_t mod_bits_count = fmpz_bits(ctx[0].divisor);
flint_bitcnt_t high_limb_bits_count = mod_bits_count % FLINT_BITS;
ulong mod_limbs_count =
mod_bits_count / FLINT_BITS + !!high_limb_bits_count;
ulong high_limb_bit_mask =
(((unsigned long long) 1) << high_limb_bits_count) - 1;
size_t rand_bytes_count = mod_limbs_count * sizeof(ulong);
ulong * rand_limbs = malloc(rand_bytes_count);
if (!rand_limbs)
abort();
do {
prng(rand_limbs, rand_bytes_count, prng_state);
if (high_limb_bits_count)
rand_limbs[mod_limbs_count - 1] &= high_limb_bit_mask;
fmpz_set_ui_array(value, rand_limbs, mod_limbs_count);
} while (fmpz_cmp(value, ctx[0].divisor) >= 0);
fmpz_sub(value, value, ctx[0].range_max);
free(rand_limbs);
}
void mod_c0_rand(fmpz_t value, mod_centered_0_ctx_t const ctx) {
mod_c0_prng(value, ctx, prng_getrandom, NULL);
}
/***
*** Simple operations on polynomials.
***/
void poly_prng_mod_c0(fmpz_poly_t res, prng_t prng, void * prng_state,
ulong coef_count, mod_centered_0_ctx_t const ctx) {
fmpz_t new_coef_value;
if (coef_count > (ulong) WORD_MAX)
abort();
fmpz_init(new_coef_value);
fmpz_poly_realloc(res, coef_count);
for (ulong coef_idx = 0; coef_idx < coef_count; coef_idx++) {
mod_c0_prng(new_coef_value, ctx, prng, prng_state);
fmpz_poly_set_coeff_fmpz(res, coef_idx, new_coef_value);
}
fmpz_clear(new_coef_value);
}
void poly_rand_mod_c0(fmpz_poly_t res, ulong coef_count,
mod_centered_0_ctx_t const ctx) {
poly_prng_mod_c0(res, prng_getrandom, NULL, coef_count, ctx);
}
bool poly_all_abs_leq(fmpz_poly_struct const * poly, fmpz const * max_coef) {
ulong length = fmpz_poly_length(poly);
fmpz_t coef;
bool result = true;
fmpz_init(coef);
for (ulong i = 0; i < length; i++) {
fmpz_poly_get_coeff_fmpz(coef, poly, i);
fmpz_abs(coef, coef);
if (fmpz_cmp(coef, max_coef) > 0) {
result = false;
break;
}
}
fmpz_clear(coef);
return result;
}
bool poly_all_abs_leq_ui(fmpz_poly_struct const * poly, ulong max_coef) {
fmpz_t max_coef_fmpz;
bool result;
fmpz_init_set_ui(max_coef_fmpz, max_coef);
result = poly_all_abs_leq(poly, max_coef_fmpz);
fmpz_clear(max_coef_fmpz);
return result;
}
/***
*** Operations in polynomial rings.
***/
void poly_ring_ctx_init(poly_ring_ctx_t ctx, mod_centered_0_ctx_t mod_ctx,
ulong divisor_degree) {
if (divisor_degree > (ulong) WORD_MAX)
abort();
mod_c0_ctx_init_set(ctx[0].mod_ctx, mod_ctx);
ctx[0].divisor_degree = divisor_degree;
}
void poly_ring_ctx_clear(poly_ring_ctx_t ctx) {
mod_c0_ctx_clear(ctx[0].mod_ctx);
}
void poly_to_ring(fmpz_poly_t poly, poly_ring_ctx_t const ctx) {
ulong degree = fmpz_poly_degree(poly);
fmpz_t new_coef_value;
fmpz_init(new_coef_value);
for (ulong coef_idx = 0; coef_idx < ctx[0].divisor_degree; coef_idx++) {
int sign = 1;
ulong higher_coef_idx = coef_idx;
fmpz_poly_get_coeff_fmpz(new_coef_value, poly, coef_idx);
/*
* Polynomial division by X^m+1 can be achieved by substituting
* -1 for X^m.
*/
do {
fmpz const * higher_coef;
sign *= -1;
if (UWORD_MAX - ctx[0].divisor_degree < higher_coef_idx)
break;
higher_coef_idx += ctx[0].divisor_degree;
if (higher_coef_idx > degree)
break;
higher_coef =
fmpz_poly_get_coeff_ptr(poly, higher_coef_idx);
(sign == 1 ? &fmpz_add : &fmpz_sub)
(new_coef_value, new_coef_value, higher_coef);
} while (true);
mod_c0(new_coef_value, ctx[0].mod_ctx);
fmpz_poly_set_coeff_fmpz(poly, coef_idx, new_coef_value);
}
if (degree >= ctx[0].divisor_degree)
fmpz_poly_realloc(poly, ctx[0].divisor_degree);
fmpz_clear(new_coef_value);
}
void poly_mul_in_ring(fmpz_poly_t res, fmpz_poly_t const poly1,
fmpz_poly_t const poly2, poly_ring_ctx_t const ctx) {
fmpz_poly_mul(res, poly1, poly2);
poly_to_ring(res, ctx);
}
void poly_add_in_ring(fmpz_poly_t res, fmpz_poly_t const poly1,
fmpz_poly_t const poly2, poly_ring_ctx_t const ctx) {
fmpz_poly_add(res, poly1, poly2);
poly_to_ring(res, ctx);
}
void poly_sub_in_ring(fmpz_poly_t res, fmpz_poly_t const poly1,
fmpz_poly_t const poly2, poly_ring_ctx_t const ctx) {
fmpz_poly_sub(res, poly1, poly2);
poly_to_ring(res, ctx);
}
void poly_prng_in_ring(fmpz_poly_t res, prng_t prng, void * prng_state,
poly_ring_ctx_t const ctx) {
poly_prng_mod_c0(res, prng, prng_state, ctx[0].divisor_degree,
ctx[0].mod_ctx);
}
void poly_rand_in_ring(fmpz_poly_t res, poly_ring_ctx_t const ctx) {
poly_prng_in_ring(res, prng_getrandom, NULL, ctx);
}
|