Coverage Report

Created: 2020-02-14 15:38

/src/botan/src/lib/pubkey/mce/code_based_key_gen.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * (C) Copyright Projet SECRET, INRIA, Rocquencourt
3
 * (C) Bhaskar Biswas and  Nicolas Sendrier
4
 *
5
 * (C) 2014 cryptosource GmbH
6
 * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
7
 * (C) 2015 Jack Lloyd
8
 *
9
 * Botan is released under the Simplified BSD License (see license.txt)
10
 *
11
 */
12
13
#include <botan/mceliece.h>
14
#include <botan/internal/mce_internal.h>
15
#include <botan/internal/code_based_util.h>
16
#include <botan/loadstor.h>
17
18
namespace Botan {
19
20
namespace {
21
22
class binary_matrix final
23
   {
24
   public:
25
      binary_matrix(size_t m_rown, size_t m_coln);
26
27
      void row_xor(size_t a, size_t b);
28
      secure_vector<size_t> row_reduced_echelon_form();
29
30
      /**
31
      * return the coefficient out of F_2
32
      */
33
      uint32_t coef(size_t i, size_t j)
34
0
         {
35
0
         return (m_elem[(i) * m_rwdcnt + (j) / 32] >> (j % 32)) & 1;
36
0
         }
37
38
      void set_coef_to_one(size_t i, size_t j)
39
0
         {
40
0
         m_elem[(i) * m_rwdcnt + (j) / 32] |= (static_cast<uint32_t>(1) << ((j) % 32)) ;
41
0
         }
42
43
      void toggle_coeff(size_t i, size_t j)
44
0
         {
45
0
         m_elem[(i) * m_rwdcnt + (j) / 32] ^= (static_cast<uint32_t>(1) << ((j) % 32)) ;
46
0
         }
47
48
0
      size_t rows() const { return m_rown; }
49
50
0
      size_t columns() const { return m_coln; }
51
52
   private:
53
      size_t m_rown;  // number of rows.
54
      size_t m_coln; // number of columns.
55
      size_t m_rwdcnt; // number of words in a row
56
   public:
57
      // TODO this should be private
58
      std::vector<uint32_t> m_elem;
59
   };
60
61
binary_matrix::binary_matrix(size_t rown, size_t coln)
62
0
   {
63
0
   m_coln = coln;
64
0
   m_rown = rown;
65
0
   m_rwdcnt = 1 + ((m_coln - 1) / 32);
66
0
   m_elem = std::vector<uint32_t>(m_rown * m_rwdcnt);
67
0
   }
68
69
void binary_matrix::row_xor(size_t a, size_t b)
70
0
   {
71
0
   for(size_t i = 0; i != m_rwdcnt; i++)
72
0
      {
73
0
      m_elem[a*m_rwdcnt+i] ^= m_elem[b*m_rwdcnt+i];
74
0
      }
75
0
   }
76
77
//the matrix is reduced from LSB...(from right)
78
secure_vector<size_t> binary_matrix::row_reduced_echelon_form()
79
0
   {
80
0
   secure_vector<size_t> perm(m_coln);
81
0
   for(size_t i = 0; i != m_coln; i++)
82
0
      {
83
0
      perm[i] = i; // initialize permutation.
84
0
      }
85
0
86
0
   uint32_t failcnt = 0;
87
0
88
0
   size_t max = m_coln - 1;
89
0
   for(size_t i = 0; i != m_rown; i++, max--)
90
0
      {
91
0
      bool found_row = false;
92
0
93
0
      for(size_t j = i; !found_row && j != m_rown; j++)
94
0
         {
95
0
         if(coef(j, max))
96
0
            {
97
0
            if(i != j) //not needed as ith row is 0 and jth row is 1.
98
0
               {
99
0
               row_xor(i, j);//xor to the row.(swap)?
100
0
               }
101
0
102
0
            found_row = true;
103
0
            }
104
0
         }
105
0
106
0
      //if no row with a 1 found then swap last column and the column with no 1 down.
107
0
      if(!found_row)
108
0
         {
109
0
         perm[m_coln - m_rown - 1 - failcnt] = static_cast<int>(max);
110
0
         failcnt++;
111
0
         if(!max)
112
0
            {
113
0
            perm.resize(0);
114
0
            }
115
0
         i--;
116
0
         }
117
0
      else
118
0
         {
119
0
         perm[i+m_coln - m_rown] = max;
120
0
         for(size_t j=i+1;j<m_rown;j++)//fill the column downwards with 0's
121
0
            {
122
0
            if(coef(j, max))
123
0
               {
124
0
               row_xor(j,i);//check the arg. order.
125
0
               }
126
0
            }
127
0
128
0
         //fill the column with 0's upwards too.
129
0
         for(size_t j = i; j != 0; --j)
130
0
            {
131
0
            if(coef(j - 1, max))
132
0
               {
133
0
               row_xor(j - 1, i);
134
0
               }
135
0
            }
136
0
         }
137
0
      }//end for(i)
138
0
   return perm;
139
0
   }
140
141
void randomize_support(std::vector<gf2m>& L, RandomNumberGenerator& rng)
142
0
   {
143
0
   for(size_t i = 0; i != L.size(); ++i)
144
0
      {
145
0
      gf2m rnd = random_gf2m(rng);
146
0
147
0
       // no rejection sampling, but for useful code-based parameters with n <= 13 this seem tolerable
148
0
      std::swap(L[i], L[rnd % L.size()]);
149
0
      }
150
0
   }
151
152
std::unique_ptr<binary_matrix> generate_R(std::vector<gf2m> &L, polyn_gf2m* g, std::shared_ptr<GF2m_Field> sp_field, size_t code_length, size_t t)
153
0
   {
154
0
   //L- Support
155
0
   //t- Number of errors
156
0
   //n- Length of the Goppa code
157
0
   //m- The extension degree of the GF
158
0
   //g- The generator polynomial.
159
0
160
0
   const size_t r = t * sp_field->get_extension_degree();
161
0
162
0
   binary_matrix H(r, code_length);
163
0
164
0
   for(size_t i = 0; i != code_length; i++)
165
0
      {
166
0
      gf2m x = g->eval(lex_to_gray(L[i]));//evaluate the polynomial at the point L[i].
167
0
      x = sp_field->gf_inv(x);
168
0
      gf2m y = x;
169
0
      for(size_t j=0;j<t;j++)
170
0
         {
171
0
         for(size_t k=0;k<sp_field->get_extension_degree();k++)
172
0
            {
173
0
            if(y & (1<<k))
174
0
               {
175
0
               //the co-eff. are set in 2^0,...,2^11 ; 2^0,...,2^11 format along the rows/cols?
176
0
               H.set_coef_to_one(j*sp_field->get_extension_degree()+ k,i);
177
0
               }
178
0
            }
179
0
         y = sp_field->gf_mul(y,lex_to_gray(L[i]));
180
0
         }
181
0
      }//The H matrix is fed.
182
0
183
0
   secure_vector<size_t> perm = H.row_reduced_echelon_form();
184
0
   if(perm.size() == 0)
185
0
      {
186
0
      throw Invalid_State("McEliece keygen failed - could not bring matrix to row reduced echelon form");
187
0
      }
188
0
189
0
   std::unique_ptr<binary_matrix> result(new binary_matrix(code_length-r, r)) ;
190
0
   for(size_t i = 0; i < result->rows(); ++i)
191
0
      {
192
0
      for(size_t j = 0; j < result->columns(); ++j)
193
0
         {
194
0
         if(H.coef(j, perm[i]))
195
0
            {
196
0
            result->toggle_coeff(i,j);
197
0
            }
198
0
         }
199
0
      }
200
0
201
0
   std::vector<gf2m> Laux(code_length);
202
0
   for(size_t i = 0; i < code_length; ++i)
203
0
      {
204
0
      Laux[i] = L[perm[i]];
205
0
      }
206
0
207
0
   for(size_t i = 0; i < code_length; ++i)
208
0
      {
209
0
      L[i] = Laux[i];
210
0
      }
211
0
   return result;
212
0
   }
213
}
214
215
McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator & rng, size_t ext_deg, size_t code_length, size_t t)
216
0
   {
217
0
   const size_t codimension = t * ext_deg;
218
0
219
0
   if(code_length <= codimension)
220
0
      {
221
0
      throw Invalid_Argument("invalid McEliece parameters");
222
0
      }
223
0
224
0
   std::shared_ptr<GF2m_Field> sp_field(new GF2m_Field(ext_deg));
225
0
226
0
   //pick the support.........
227
0
   std::vector<gf2m> L(code_length);
228
0
229
0
   for(size_t i = 0; i != L.size(); i++)
230
0
      {
231
0
      L[i] = static_cast<gf2m>(i);
232
0
      }
233
0
   randomize_support(L, rng);
234
0
   polyn_gf2m g(sp_field); // create as zero
235
0
236
0
   bool success = false;
237
0
   std::unique_ptr<binary_matrix> R;
238
0
239
0
   do
240
0
      {
241
0
      // create a random irreducible polynomial
242
0
      g = polyn_gf2m(t, rng, sp_field);
243
0
244
0
      try
245
0
         {
246
0
         R = generate_R(L, &g, sp_field, code_length, t);
247
0
         success = true;
248
0
         }
249
0
      catch(const Invalid_State &)
250
0
         {
251
0
         }
252
0
      } while (!success);
253
0
254
0
   std::vector<polyn_gf2m> sqrtmod = polyn_gf2m::sqrt_mod_init( g);
255
0
   std::vector<polyn_gf2m> F = syndrome_init(g, L, static_cast<int>(code_length));
256
0
257
0
   // Each F[i] is the (precomputed) syndrome of the error vector with
258
0
   // a single '1' in i-th position.
259
0
   // We do not store the F[i] as polynomials of degree t , but
260
0
   // as binary vectors of length ext_deg * t (this will
261
0
   // speed up the syndrome computation)
262
0
   //
263
0
   std::vector<uint32_t> H(bit_size_to_32bit_size(codimension) * code_length);
264
0
   uint32_t* sk = H.data();
265
0
   for(size_t i = 0; i < code_length; ++i)
266
0
      {
267
0
      for(size_t l = 0; l < t; ++l)
268
0
         {
269
0
         const size_t k = (l * ext_deg) / 32;
270
0
         const uint8_t j = (l * ext_deg) % 32;
271
0
         sk[k] ^= static_cast<uint32_t>(F[i].get_coef(l)) << j;
272
0
         if(j + ext_deg > 32)
273
0
            {
274
0
            sk[k + 1] ^= F[i].get_coef(l) >> (32 - j);
275
0
            }
276
0
         }
277
0
      sk += bit_size_to_32bit_size(codimension);
278
0
      }
279
0
280
0
   // We need the support L for decoding (decryption). In fact the
281
0
   // inverse is needed
282
0
283
0
   std::vector<gf2m> Linv(code_length) ;
284
0
   for(size_t i = 0; i != Linv.size(); ++i)
285
0
      {
286
0
      Linv[L[i]] = static_cast<gf2m>(i);
287
0
      }
288
0
   std::vector<uint8_t> pubmat(R->m_elem.size() * 4);
289
0
   for(size_t i = 0; i < R->m_elem.size(); i++)
290
0
      {
291
0
      store_le(R->m_elem[i], &pubmat[i*4]);
292
0
      }
293
0
294
0
   return McEliece_PrivateKey(g, H, sqrtmod, Linv, pubmat);
295
0
   }
296
297
}