Coverage Report

Created: 2024-11-29 06:10

/src/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * (C) 2014 cryptosource GmbH
3
 * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
4
 *
5
 * Botan is released under the Simplified BSD License (see license.txt)
6
 *
7
 */
8
9
#include <botan/internal/polyn_gf2m.h>
10
11
#include <botan/exceptn.h>
12
#include <botan/internal/bit_ops.h>
13
#include <botan/internal/code_based_util.h>
14
15
namespace Botan {
16
17
namespace {
18
19
0
void patch_root_array(gf2m res_root_arr[], size_t res_root_arr_len, size_t root_pos) {
20
0
   volatile gf2m patch_elem = 0x01;
21
0
   volatile gf2m cond_mask = (root_pos == res_root_arr_len);
22
0
   cond_mask = expand_mask_16bit(cond_mask);
23
0
   cond_mask = ~cond_mask; /* now cond = 1 if not enough roots */
24
0
   patch_elem = patch_elem & cond_mask;
25
0
   for(size_t i = 0; i < res_root_arr_len; i++) {
26
0
      patch_elem = patch_elem + 1;
27
0
      gf2m masked_patch_elem = patch_elem & cond_mask;
28
0
      res_root_arr[i] ^= masked_patch_elem++;
29
0
   }
30
0
}
31
32
class gf2m_decomp_rootfind_state {
33
   public:
34
      gf2m_decomp_rootfind_state(const polyn_gf2m& p_polyn, size_t code_length);
35
36
      void calc_LiK(const polyn_gf2m& sigma);
37
      gf2m calc_Fxj_j_neq_0(const polyn_gf2m& sigma, gf2m j_gray);
38
      void calc_next_Aij();
39
      void calc_Ai_zero(const polyn_gf2m& sigma);
40
      secure_vector<gf2m> find_roots(const polyn_gf2m& sigma);
41
42
   private:
43
      size_t m_code_length;
44
      secure_vector<gf2m> m_Lik;  // size is outer_summands * m
45
      secure_vector<gf2m> m_Aij;  // ...
46
      uint32_t m_outer_summands;
47
      gf2m m_j;
48
      gf2m m_j_gray;
49
      gf2m m_sigma_3_l;
50
      gf2m m_sigma_3_neq_0_mask;
51
};
52
53
/**
54
* calculates ceil((t-4)/5) = outer_summands - 1
55
*/
56
0
uint32_t brootf_decomp_calc_sum_limit(uint32_t t) {
57
0
   uint32_t result;
58
0
   if(t < 4) {
59
0
      return 0;
60
0
   }
61
0
   result = t - 4;
62
0
   result += 4;
63
0
   result /= 5;
64
0
   return result;
65
0
}
66
67
gf2m_decomp_rootfind_state::gf2m_decomp_rootfind_state(const polyn_gf2m& polyn, size_t code_length) :
68
0
      m_code_length(code_length), m_j(0), m_j_gray(0) {
69
0
   gf2m coeff_3;
70
0
   gf2m coeff_head;
71
0
   std::shared_ptr<GF2m_Field> sp_field = polyn.get_sp_field();
72
0
   int deg_sigma = polyn.get_degree();
73
0
   if(deg_sigma <= 3) {
74
0
      throw Internal_Error("Unexpected degree in gf2m_decomp_rootfind_state");
75
0
   }
76
77
0
   coeff_3 = polyn.get_coef(3);
78
0
   coeff_head = polyn.get_coef(deg_sigma); /* dummy value for SCA CM */
79
0
   if(coeff_3 != 0) {
80
0
      this->m_sigma_3_l = sp_field->gf_l_from_n(coeff_3);
81
0
      this->m_sigma_3_neq_0_mask = 0xFFFF;
82
0
   } else {
83
      // dummy value needed for timing countermeasure
84
0
      this->m_sigma_3_l = sp_field->gf_l_from_n(coeff_head);
85
0
      this->m_sigma_3_neq_0_mask = 0;
86
0
   }
87
88
0
   this->m_outer_summands = 1 + brootf_decomp_calc_sum_limit(deg_sigma);
89
0
   this->m_Lik.resize(this->m_outer_summands * sp_field->get_extension_degree());
90
0
   this->m_Aij.resize(this->m_outer_summands);
91
0
}
92
93
0
void gf2m_decomp_rootfind_state::calc_Ai_zero(const polyn_gf2m& sigma) {
94
0
   uint32_t i;
95
   /*
96
   * this function assumes this the first gray code element is zero
97
   */
98
0
   for(i = 0; i < this->m_outer_summands; i++) {
99
0
      this->m_Aij[i] = sigma.get_coef(5 * i);
100
0
   }
101
0
   this->m_j = 0;
102
0
   this->m_j_gray = 0;
103
0
}
104
105
0
void gf2m_decomp_rootfind_state::calc_next_Aij() {
106
   /*
107
   * upon function entry, we have in the state j, Aij.
108
   * first thing, we declare Aij Aij_minusone and increase j.
109
   * Case j=0 upon function entry also included, then Aij contains A_{i,j=0}.
110
   */
111
0
   uint32_t i;
112
0
   gf2m diff, new_j_gray;
113
0
   uint32_t Lik_pos_base;
114
115
0
   this->m_j++;
116
117
0
   new_j_gray = lex_to_gray(this->m_j);
118
119
0
   if(this->m_j & 1) /* half of the times */
120
0
   {
121
0
      Lik_pos_base = 0;
122
0
   } else if(this->m_j & 2) /* one quarter of the times */
123
0
   {
124
0
      Lik_pos_base = this->m_outer_summands;
125
0
   } else if(this->m_j & 4) /* one eighth of the times */
126
0
   {
127
0
      Lik_pos_base = this->m_outer_summands * 2;
128
0
   } else if(this->m_j & 8) /* one sixteenth of the times */
129
0
   {
130
0
      Lik_pos_base = this->m_outer_summands * 3;
131
0
   } else if(this->m_j & 16) /* ... */
132
0
   {
133
0
      Lik_pos_base = this->m_outer_summands * 4;
134
0
   } else {
135
0
      gf2m delta_offs = 5;
136
0
      diff = this->m_j_gray ^ new_j_gray;
137
0
      while(((static_cast<gf2m>(1) << delta_offs) & diff) == 0) {
138
0
         delta_offs++;
139
0
      }
140
0
      Lik_pos_base = delta_offs * this->m_outer_summands;
141
0
   }
142
0
   this->m_j_gray = new_j_gray;
143
144
0
   i = 0;
145
0
   for(; i < this->m_outer_summands; i++) {
146
0
      this->m_Aij[i] ^= this->m_Lik[Lik_pos_base + i];
147
0
   }
148
0
}
149
150
0
void gf2m_decomp_rootfind_state::calc_LiK(const polyn_gf2m& sigma) {
151
0
   std::shared_ptr<GF2m_Field> sp_field = sigma.get_sp_field();
152
0
   uint32_t i, k, d;
153
0
   d = sigma.get_degree();
154
0
   for(k = 0; k < sp_field->get_extension_degree(); k++) {
155
0
      uint32_t Lik_pos_base = k * this->m_outer_summands;
156
0
      gf2m alpha_l_k_tt2_ttj[4];
157
0
      alpha_l_k_tt2_ttj[0] = sp_field->gf_l_from_n(static_cast<gf2m>(1) << k);
158
0
      alpha_l_k_tt2_ttj[1] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[0], alpha_l_k_tt2_ttj[0]);
159
0
      alpha_l_k_tt2_ttj[2] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[1], alpha_l_k_tt2_ttj[1]);
160
161
0
      alpha_l_k_tt2_ttj[3] = sp_field->gf_mul_rrr(alpha_l_k_tt2_ttj[2], alpha_l_k_tt2_ttj[2]);
162
0
      for(i = 0; i < this->m_outer_summands; i++) {
163
0
         uint32_t j;
164
0
         uint32_t five_i = 5 * i;
165
0
         uint32_t Lik_pos = Lik_pos_base + i;
166
0
         this->m_Lik[Lik_pos] = 0;
167
0
         for(j = 0; j <= 3; j++) {
168
0
            gf2m f, x;
169
0
            uint32_t f_ind = five_i + (static_cast<uint32_t>(1) << j);
170
0
            if(f_ind > d) {
171
0
               break;
172
0
            }
173
0
            f = sigma.get_coef(f_ind);
174
175
0
            x = sp_field->gf_mul_zrz(alpha_l_k_tt2_ttj[j], f);
176
0
            this->m_Lik[Lik_pos] ^= x;
177
0
         }
178
0
      }
179
0
   }
180
0
}
181
182
0
gf2m gf2m_decomp_rootfind_state::calc_Fxj_j_neq_0(const polyn_gf2m& sigma, gf2m j_gray) {
183
   //needs the A_{ij} to compute F(x)_j
184
0
   gf2m sum = 0;
185
0
   uint32_t i;
186
0
   std::shared_ptr<GF2m_Field> sp_field = sigma.get_sp_field();
187
0
   const gf2m jl_gray = sp_field->gf_l_from_n(j_gray);
188
0
   gf2m xl_j_tt_5 = sp_field->gf_square_rr(jl_gray);
189
0
   gf2m xl_gray_tt_3 = sp_field->gf_mul_rrr(xl_j_tt_5, jl_gray);
190
0
   xl_j_tt_5 = sp_field->gf_mul_rrr(xl_j_tt_5, xl_gray_tt_3);
191
192
0
   sum = sp_field->gf_mul_nrr(xl_gray_tt_3, this->m_sigma_3_l);
193
0
   sum &= this->m_sigma_3_neq_0_mask;
194
   /* here, we rely on compiler to be unable to optimize
195
   * for the state->sigma_3_neq_0_mask value
196
   */
197
   /* treat i = 0 special: */
198
0
   sum ^= this->m_Aij[0];
199
   /* treat i = 1 special also */
200
201
0
   if(this->m_outer_summands > 1) {
202
0
      gf2m x;
203
0
      x = sp_field->gf_mul_zrz(xl_j_tt_5, this->m_Aij[1]); /* x_j^{5i} A_i^j */
204
0
      sum ^= x;
205
0
   }
206
207
0
   gf2m xl_j_tt_5i = xl_j_tt_5;
208
209
0
   for(i = 2; i < this->m_outer_summands; i++) {
210
0
      gf2m x;
211
0
      xl_j_tt_5i = sp_field->gf_mul_rrr(xl_j_tt_5i, xl_j_tt_5);
212
      // now x_j_tt_5i lives up to its name
213
0
      x = sp_field->gf_mul_zrz(xl_j_tt_5i, this->m_Aij[i]); /* x_j^{5i} A_i^(j) */
214
0
      sum ^= x;
215
0
   }
216
0
   return sum;
217
0
}
218
219
0
secure_vector<gf2m> gf2m_decomp_rootfind_state::find_roots(const polyn_gf2m& sigma) {
220
0
   const int sigma_degree = sigma.get_degree();
221
0
   BOTAN_ASSERT(sigma_degree > 0, "Valid sigma");
222
0
   secure_vector<gf2m> result(sigma_degree);
223
0
   uint32_t root_pos = 0;
224
225
0
   this->calc_Ai_zero(sigma);
226
0
   this->calc_LiK(sigma);
227
0
   for(;;) {
228
0
      gf2m eval_result;
229
230
0
      if(this->m_j_gray == 0) {
231
0
         eval_result = sigma.get_coef(0);
232
0
      } else {
233
0
         eval_result = this->calc_Fxj_j_neq_0(sigma, this->m_j_gray);
234
0
      }
235
236
0
      if(eval_result == 0) {
237
0
         result[root_pos] = this->m_j_gray;
238
0
         root_pos++;
239
0
      }
240
241
0
      if(this->m_j + static_cast<uint32_t>(1) == m_code_length) {
242
0
         break;
243
0
      }
244
0
      this->calc_next_Aij();
245
0
   }
246
247
   // side channel / fault attack countermeasure:
248
0
   patch_root_array(result.data(), result.size(), root_pos);
249
0
   return result;
250
0
}
251
252
}  // end anonymous namespace
253
254
0
secure_vector<gf2m> find_roots_gf2m_decomp(const polyn_gf2m& polyn, size_t code_length) {
255
0
   gf2m_decomp_rootfind_state state(polyn, code_length);
256
0
   return state.find_roots(polyn);
257
0
}
258
259
}  // end namespace Botan