Botan 3.4.0
Crypto and TLS for C&
code_based_key_gen.cpp
Go to the documentation of this file.
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
15#include <botan/internal/code_based_util.h>
16#include <botan/internal/loadstor.h>
17#include <botan/internal/mce_internal.h>
18#include <botan/internal/polyn_gf2m.h>
19
20namespace Botan {
21
22namespace {
23
24class binary_matrix final {
25 public:
26 binary_matrix(size_t m_rown, size_t m_coln);
27
28 void row_xor(size_t a, size_t b);
29 secure_vector<size_t> row_reduced_echelon_form();
30
31 /**
32 * return the coefficient out of F_2
33 */
34 uint32_t coef(size_t i, size_t j) { return (m_elem[(i)*m_rwdcnt + (j) / 32] >> (j % 32)) & 1; }
35
36 void set_coef_to_one(size_t i, size_t j) {
37 m_elem[(i)*m_rwdcnt + (j) / 32] |= (static_cast<uint32_t>(1) << ((j) % 32));
38 }
39
40 void toggle_coeff(size_t i, size_t j) {
41 m_elem[(i)*m_rwdcnt + (j) / 32] ^= (static_cast<uint32_t>(1) << ((j) % 32));
42 }
43
44 size_t rows() const { return m_rown; }
45
46 size_t columns() const { return m_coln; }
47
48 const std::vector<uint32_t>& elem() const { return m_elem; }
49
50 private:
51 size_t m_rown; // number of rows.
52 size_t m_coln; // number of columns.
53 size_t m_rwdcnt; // number of words in a row
54 std::vector<uint32_t> m_elem;
55};
56
57binary_matrix::binary_matrix(size_t rown, size_t coln) {
58 m_coln = coln;
59 m_rown = rown;
60 m_rwdcnt = 1 + ((m_coln - 1) / 32);
61 m_elem = std::vector<uint32_t>(m_rown * m_rwdcnt);
62}
63
64void binary_matrix::row_xor(size_t a, size_t b) {
65 for(size_t i = 0; i != m_rwdcnt; i++) {
66 m_elem[a * m_rwdcnt + i] ^= m_elem[b * m_rwdcnt + i];
67 }
68}
69
70//the matrix is reduced from LSB...(from right)
71secure_vector<size_t> binary_matrix::row_reduced_echelon_form() {
72 secure_vector<size_t> perm(m_coln);
73 for(size_t i = 0; i != m_coln; i++) {
74 perm[i] = i; // initialize permutation.
75 }
76
77 uint32_t failcnt = 0;
78
79 size_t max = m_coln - 1;
80 for(size_t i = 0; i != m_rown; i++, max--) {
81 bool found_row = false;
82
83 for(size_t j = i; !found_row && j != m_rown; j++) {
84 if(coef(j, max)) {
85 if(i != j) //not needed as ith row is 0 and jth row is 1.
86 {
87 row_xor(i, j); //xor to the row.(swap)?
88 }
89
90 found_row = true;
91 }
92 }
93
94 //if no row with a 1 found then swap last column and the column with no 1 down.
95 if(!found_row) {
96 perm[m_coln - m_rown - 1 - failcnt] = static_cast<int>(max);
97 failcnt++;
98 if(!max) {
99 perm.clear();
100 }
101 i--;
102 } else {
103 perm[i + m_coln - m_rown] = max;
104 for(size_t j = i + 1; j < m_rown; j++) //fill the column downwards with 0's
105 {
106 if(coef(j, max)) {
107 row_xor(j, i); //check the arg. order.
108 }
109 }
110
111 //fill the column with 0's upwards too.
112 for(size_t j = i; j != 0; --j) {
113 if(coef(j - 1, max)) {
114 row_xor(j - 1, i);
115 }
116 }
117 }
118 } //end for(i)
119 return perm;
120}
121
122void randomize_support(std::vector<gf2m>& L, RandomNumberGenerator& rng) {
123 for(size_t i = 0; i != L.size(); ++i) {
124 gf2m rnd = random_gf2m(rng);
125
126 // no rejection sampling, but for useful code-based parameters with n <= 13 this seem tolerable
127 std::swap(L[i], L[rnd % L.size()]);
128 }
129}
130
131std::unique_ptr<binary_matrix> generate_R(
132 std::vector<gf2m>& L, polyn_gf2m* g, const GF2m_Field& sp_field, size_t code_length, size_t t) {
133 //L- Support
134 //t- Number of errors
135 //n- Length of the Goppa code
136 //m- The extension degree of the GF
137 //g- The generator polynomial.
138
139 const size_t r = t * sp_field.get_extension_degree();
140
141 binary_matrix H(r, code_length);
142
143 for(size_t i = 0; i != code_length; i++) {
144 gf2m x = g->eval(lex_to_gray(L[i])); //evaluate the polynomial at the point L[i].
145 x = sp_field.gf_inv(x);
146 gf2m y = x;
147 for(size_t j = 0; j < t; j++) {
148 for(size_t k = 0; k < sp_field.get_extension_degree(); k++) {
149 if(y & (1 << k)) {
150 //the co-eff. are set in 2^0,...,2^11 ; 2^0,...,2^11 format along the rows/cols?
151 H.set_coef_to_one(j * sp_field.get_extension_degree() + k, i);
152 }
153 }
154 y = sp_field.gf_mul(y, lex_to_gray(L[i]));
155 }
156 } //The H matrix is fed.
157
158 secure_vector<size_t> perm = H.row_reduced_echelon_form();
159 if(perm.empty()) {
160 throw Invalid_State("McEliece keygen failed - could not bring matrix to row reduced echelon form");
161 }
162
163 auto result = std::make_unique<binary_matrix>(code_length - r, r);
164 for(size_t i = 0; i < result->rows(); ++i) {
165 for(size_t j = 0; j < result->columns(); ++j) {
166 if(H.coef(j, perm[i])) {
167 result->toggle_coeff(i, j);
168 }
169 }
170 }
171
172 std::vector<gf2m> Laux(code_length);
173 for(size_t i = 0; i < code_length; ++i) {
174 Laux[i] = L[perm[i]];
175 }
176
177 for(size_t i = 0; i < code_length; ++i) {
178 L[i] = Laux[i];
179 }
180 return result;
181}
182} // namespace
183
184McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator& rng, size_t ext_deg, size_t code_length, size_t t) {
185 const size_t codimension = t * ext_deg;
186
187 if(code_length <= codimension) {
188 throw Invalid_Argument("invalid McEliece parameters");
189 }
190
191 auto sp_field = std::make_shared<GF2m_Field>(ext_deg);
192
193 //pick the support.........
194 std::vector<gf2m> L(code_length);
195
196 for(size_t i = 0; i != L.size(); i++) {
197 L[i] = static_cast<gf2m>(i);
198 }
199 randomize_support(L, rng);
200 polyn_gf2m g(sp_field); // create as zero
201
202 bool success = false;
203 std::unique_ptr<binary_matrix> R;
204
205 do {
206 // create a random irreducible polynomial
207 g = polyn_gf2m(t, rng, sp_field);
208
209 try {
210 R = generate_R(L, &g, *sp_field, code_length, t);
211 success = true;
212 } catch(const Invalid_State&) {}
213 } while(!success);
214
215 std::vector<polyn_gf2m> sqrtmod = polyn_gf2m::sqrt_mod_init(g);
216 std::vector<polyn_gf2m> F = syndrome_init(g, L, static_cast<int>(code_length));
217
218 // Each F[i] is the (precomputed) syndrome of the error vector with
219 // a single '1' in i-th position.
220 // We do not store the F[i] as polynomials of degree t , but
221 // as binary vectors of length ext_deg * t (this will
222 // speed up the syndrome computation)
223 //
224 std::vector<uint32_t> H(bit_size_to_32bit_size(codimension) * code_length);
225 uint32_t* sk = H.data();
226 for(size_t i = 0; i < code_length; ++i) {
227 for(size_t l = 0; l < t; ++l) {
228 const size_t k = (l * ext_deg) / 32;
229 const uint8_t j = (l * ext_deg) % 32;
230 sk[k] ^= static_cast<uint32_t>(F[i].get_coef(l)) << j;
231 if(j + ext_deg > 32) {
232 sk[k + 1] ^= F[i].get_coef(l) >> (32 - j);
233 }
234 }
235 sk += bit_size_to_32bit_size(codimension);
236 }
237
238 // We need the support L for decoding (decryption). In fact the
239 // inverse is needed
240
241 std::vector<gf2m> Linv(code_length);
242 for(size_t i = 0; i != Linv.size(); ++i) {
243 Linv[L[i]] = static_cast<gf2m>(i);
244 }
245 std::vector<uint8_t> pubmat(R->elem().size() * 4);
246 for(size_t i = 0; i < R->elem().size(); i++) {
247 store_le(R->elem()[i], &pubmat[i * 4]);
248 }
249
250 return McEliece_PrivateKey(g, H, sqrtmod, Linv, pubmat);
251}
252
253} // namespace Botan
static std::vector< polyn_gf2m > sqrt_mod_init(const polyn_gf2m &g)
int(* final)(unsigned char *, CTX *)
gf2m lex_to_gray(gf2m lex)
std::vector< polyn_gf2m > syndrome_init(const polyn_gf2m &generator, const std::vector< gf2m > &support, int n)
McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator &rng, size_t ext_deg, size_t code_length, size_t t)
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:702
gf2m random_gf2m(RandomNumberGenerator &rng)
size_t bit_size_to_32bit_size(size_t bit_size)
uint16_t gf2m