/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 | | } |