Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

346 рядки
10 KiB

  1. #include "poly.h"
  2. #include "fips202.h"
  3. #include "verify.h"
  4. uint16_t PQCLEAN_NTRUHPS4096821_CLEAN_mod3(uint16_t a) {
  5. uint16_t r;
  6. int16_t t, c;
  7. r = (a >> 8) + (a & 0xff); // r mod 255 == a mod 255
  8. r = (r >> 4) + (r & 0xf); // r' mod 15 == r mod 15
  9. r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3
  10. r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3
  11. t = r - 3;
  12. c = t >> 15;
  13. return (c & r) ^ (~c & t);
  14. }
  15. /* Map {0, 1, 2} -> {0,1,q-1} in place */
  16. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(poly *r) {
  17. int i;
  18. for (i = 0; i < NTRU_N; i++) {
  19. r->coeffs[i] = r->coeffs[i] | ((-(r->coeffs[i] >> 1)) & (NTRU_Q - 1));
  20. }
  21. }
  22. /* Map {0, 1, q-1} -> {0,1,2} in place */
  23. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_trinary_Zq_to_Z3(poly *r) {
  24. int i;
  25. for (i = 0; i < NTRU_N; i++) {
  26. r->coeffs[i] = 3 & (r->coeffs[i] ^ (r->coeffs[i] >> (NTRU_LOGQ - 1)));
  27. }
  28. }
  29. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b) {
  30. int k, i;
  31. for (k = 0; k < NTRU_N; k++) {
  32. r->coeffs[k] = 0;
  33. for (i = 1; i < NTRU_N - k; i++) {
  34. r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i];
  35. }
  36. for (i = 0; i < k + 1; i++) {
  37. r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i];
  38. }
  39. r->coeffs[k] = MODQ(r->coeffs[k]);
  40. }
  41. }
  42. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b) {
  43. int i;
  44. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(r, a, b);
  45. for (i = 0; i < NTRU_N; i++) {
  46. r->coeffs[i] = MODQ(r->coeffs[i] - r->coeffs[NTRU_N - 1]);
  47. }
  48. }
  49. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b) {
  50. int k, i;
  51. for (k = 0; k < NTRU_N; k++) {
  52. r->coeffs[k] = 0;
  53. for (i = 1; i < NTRU_N - k; i++) {
  54. r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i];
  55. }
  56. for (i = 0; i < k + 1; i++) {
  57. r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i];
  58. }
  59. }
  60. for (k = 0; k < NTRU_N; k++) {
  61. r->coeffs[k] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[k] + 2 * r->coeffs[NTRU_N - 1]);
  62. }
  63. }
  64. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a) {
  65. int i;
  66. uint16_t last_coeff = a->coeffs[NTRU_N - 1];
  67. for (i = NTRU_N - 1; i > 0; i--) {
  68. r->coeffs[i] = MODQ(a->coeffs[i - 1] + (NTRU_Q - a->coeffs[i]));
  69. }
  70. r->coeffs[0] = MODQ(last_coeff + (NTRU_Q - a->coeffs[0]));
  71. }
  72. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_lift(poly *r, const poly *a) {
  73. int i;
  74. for (i = 0; i < NTRU_N; i++) {
  75. r->coeffs[i] = a->coeffs[i];
  76. }
  77. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(r);
  78. }
  79. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_to_S3(poly *r, const poly *a) {
  80. /* NOTE: Assumes input is in [0,Q-1]^N */
  81. /* Produces output in {0,1,2}^N */
  82. int i;
  83. /* Center coeffs around 3Q: [0, Q-1] -> [3Q - Q/2, 3Q + Q/2) */
  84. for (i = 0; i < NTRU_N; i++) {
  85. r->coeffs[i] = ((a->coeffs[i] >> (NTRU_LOGQ - 1)) ^ 3) << NTRU_LOGQ;
  86. r->coeffs[i] += a->coeffs[i];
  87. }
  88. /* Reduce mod (3, Phi) */
  89. r->coeffs[NTRU_N - 1] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[NTRU_N - 1]);
  90. for (i = 0; i < NTRU_N; i++) {
  91. r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]);
  92. }
  93. }
  94. #define POLY_R2_ADD(I,A,B,S) \
  95. for ((I)=0; (I)<NTRU_N; (I)++) { \
  96. (A).coeffs[(I)] ^= (B).coeffs[(I)] * (S); \
  97. }
  98. static void cswappoly(poly *a, poly *b, int swap) {
  99. int i;
  100. uint16_t t;
  101. swap = -swap;
  102. for (i = 0; i < NTRU_N; i++) {
  103. t = (a->coeffs[i] ^ b->coeffs[i]) & swap;
  104. a->coeffs[i] ^= t;
  105. b->coeffs[i] ^= t;
  106. }
  107. }
  108. static inline void poly_divx(poly *a, int s) {
  109. int i;
  110. for (i = 1; i < NTRU_N; i++) {
  111. a->coeffs[i - 1] = (unsigned char) ((s * a->coeffs[i]) | (!s * a->coeffs[i - 1]));
  112. }
  113. a->coeffs[NTRU_N - 1] = (!s * a->coeffs[NTRU_N - 1]);
  114. }
  115. static inline void poly_mulx(poly *a, int s) {
  116. int i;
  117. for (i = 1; i < NTRU_N; i++) {
  118. a->coeffs[NTRU_N - i] = (unsigned char) ((s * a->coeffs[NTRU_N - i - 1]) | (!s * a->coeffs[NTRU_N - i]));
  119. }
  120. a->coeffs[0] = (!s * a->coeffs[0]);
  121. }
  122. static void poly_R2_inv(poly *r, const poly *a) {
  123. /* Schroeppel--Orman--O'Malley--Spatscheck
  124. * "Almost Inverse" algorithm as described
  125. * by Silverman in NTRU Tech Report #14 */
  126. // with several modifications to make it run in constant-time
  127. int i, j;
  128. int k = 0;
  129. uint16_t degf = NTRU_N - 1;
  130. uint16_t degg = NTRU_N - 1;
  131. int sign, t, swap;
  132. int16_t done = 0;
  133. poly b, f, g;
  134. poly *c = r; // save some stack space
  135. poly *temp_r = &f;
  136. /* b(X) := 1 */
  137. for (i = 1; i < NTRU_N; i++) {
  138. b.coeffs[i] = 0;
  139. }
  140. b.coeffs[0] = 1;
  141. /* c(X) := 0 */
  142. for (i = 0; i < NTRU_N; i++) {
  143. c->coeffs[i] = 0;
  144. }
  145. /* f(X) := a(X) */
  146. for (i = 0; i < NTRU_N; i++) {
  147. f.coeffs[i] = a->coeffs[i] & 1;
  148. }
  149. /* g(X) := 1 + X + X^2 + ... + X^{N-1} */
  150. for (i = 0; i < NTRU_N; i++) {
  151. g.coeffs[i] = 1;
  152. }
  153. for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) {
  154. sign = f.coeffs[0];
  155. swap = sign & !done & ((degf - degg) >> 15);
  156. cswappoly(&f, &g, swap);
  157. cswappoly(&b, c, swap);
  158. t = (degf ^ degg) & (-swap);
  159. degf ^= t;
  160. degg ^= t;
  161. POLY_R2_ADD(i, f, g, sign * (!done));
  162. POLY_R2_ADD(i, b, (*c), sign * (!done));
  163. poly_divx(&f, !done);
  164. poly_mulx(c, !done);
  165. degf -= !done;
  166. k += !done;
  167. done = 1 - (((uint16_t) - degf) >> 15);
  168. }
  169. k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15);
  170. /* Return X^{N-k} * b(X) */
  171. /* This is a k-coefficient rotation. We do this by looking at the binary
  172. representation of k, rotating for every power of 2, and performing a cmov
  173. if the respective bit is set. */
  174. for (i = 0; i < NTRU_N; i++) {
  175. r->coeffs[i] = b.coeffs[i];
  176. }
  177. for (i = 0; i < 10; i++) {
  178. for (j = 0; j < NTRU_N; j++) {
  179. temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N];
  180. }
  181. PQCLEAN_NTRUHPS4096821_CLEAN_cmov((unsigned char *) & (r->coeffs),
  182. (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1);
  183. k >>= 1;
  184. }
  185. }
  186. static void poly_R2_inv_to_Rq_inv(poly *r, const poly *ai, const poly *a) {
  187. int i;
  188. poly b, c;
  189. poly s;
  190. // for 0..4
  191. // ai = ai * (2 - a*ai) mod q
  192. for (i = 0; i < NTRU_N; i++) {
  193. b.coeffs[i] = MODQ(NTRU_Q - a->coeffs[i]); // b = -a
  194. }
  195. for (i = 0; i < NTRU_N; i++) {
  196. r->coeffs[i] = ai->coeffs[i];
  197. }
  198. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, r, &b);
  199. c.coeffs[0] += 2; // c = 2 - a*ai
  200. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&s, &c, r); // s = ai*c
  201. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, &s, &b);
  202. c.coeffs[0] += 2; // c = 2 - a*s
  203. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c
  204. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, r, &b);
  205. c.coeffs[0] += 2; // c = 2 - a*r
  206. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&s, &c, r); // s = r*c
  207. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, &s, &b);
  208. c.coeffs[0] += 2; // c = 2 - a*s
  209. PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c
  210. }
  211. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_inv(poly *r, const poly *a) {
  212. poly ai2;
  213. poly_R2_inv(&ai2, a);
  214. poly_R2_inv_to_Rq_inv(r, &ai2, a);
  215. }
  216. void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_inv(poly *r, const poly *a) {
  217. /* Schroeppel--Orman--O'Malley--Spatscheck
  218. * "Almost Inverse" algorithm as described
  219. * by Silverman in NTRU Tech Report #14 */
  220. // with several modifications to make it run in constant-time
  221. int i, j;
  222. uint16_t k = 0;
  223. uint16_t degf = NTRU_N - 1;
  224. uint16_t degg = NTRU_N - 1;
  225. int sign, fsign = 0, t, swap;
  226. int16_t done = 0;
  227. poly b, c, f, g;
  228. poly *temp_r = &f;
  229. /* b(X) := 1 */
  230. for (i = 1; i < NTRU_N; i++) {
  231. b.coeffs[i] = 0;
  232. }
  233. b.coeffs[0] = 1;
  234. /* c(X) := 0 */
  235. for (i = 0; i < NTRU_N; i++) {
  236. c.coeffs[i] = 0;
  237. }
  238. /* f(X) := a(X) */
  239. for (i = 0; i < NTRU_N; i++) {
  240. f.coeffs[i] = a->coeffs[i];
  241. }
  242. /* g(X) := 1 + X + X^2 + ... + X^{N-1} */
  243. for (i = 0; i < NTRU_N; i++) {
  244. g.coeffs[i] = 1;
  245. }
  246. for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) {
  247. sign = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(2 * g.coeffs[0] * f.coeffs[0]);
  248. swap = (((sign & 2) >> 1) | sign) & !done & ((degf - degg) >> 15);
  249. cswappoly(&f, &g, swap);
  250. cswappoly(&b, &c, swap);
  251. t = (degf ^ degg) & (-swap);
  252. degf ^= t;
  253. degg ^= t;
  254. for (i = 0; i < NTRU_N; i++) {
  255. f.coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(f.coeffs[i] + ((uint16_t) (sign * (!done))) * g.coeffs[i]);
  256. }
  257. for (i = 0; i < NTRU_N; i++) {
  258. b.coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(b.coeffs[i] + ((uint16_t) (sign * (!done))) * c.coeffs[i]);
  259. }
  260. poly_divx(&f, !done);
  261. poly_mulx(&c, !done);
  262. degf -= !done;
  263. k += !done;
  264. done = 1 - (((uint16_t) - degf) >> 15);
  265. }
  266. fsign = f.coeffs[0];
  267. k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15);
  268. /* Return X^{N-k} * b(X) */
  269. /* This is a k-coefficient rotation. We do this by looking at the binary
  270. representation of k, rotating for every power of 2, and performing a cmov
  271. if the respective bit is set. */
  272. for (i = 0; i < NTRU_N; i++) {
  273. r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3((uint16_t) fsign * b.coeffs[i]);
  274. }
  275. for (i = 0; i < 10; i++) {
  276. for (j = 0; j < NTRU_N; j++) {
  277. temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N];
  278. }
  279. PQCLEAN_NTRUHPS4096821_CLEAN_cmov((unsigned char *) & (r->coeffs),
  280. (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1);
  281. k >>= 1;
  282. }
  283. /* Reduce modulo Phi_n */
  284. for (i = 0; i < NTRU_N; i++) {
  285. r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]);
  286. }
  287. }