int pubkey::encrypt (const bvector&in, bvector&out, const bvector&errors) { if (in.size() != plain_size() ) return 2; if (errors.size() != cipher_size() ) return 2; G.mult_vecT_left (in, out); out.add (errors); return 0; }
int privkey::decrypt (const bvector & in, bvector & out, bvector & errors) { if (in.size() != cipher_size() ) return 2; polynomial synd; uint i, j, tmp; /* * compute the syndrome from alternant check matrix * that is H_alt = Vdm(L) * Diag(g(L_i)^{-2}) */ uint h_size = 1 << (T + 1); //= 2*block_size synd.clear(); synd.resize (h_size, 0); for (i = 0; i < cipher_size(); ++i) if (in[i]) { tmp = fld.inv (g.eval (permuted_support[i], fld) ); tmp = fld.mult (tmp, tmp); //g(Li)^{-2} synd[0] = fld.add (synd[0], tmp); for (j = 1; j < h_size; ++j) { tmp = fld.mult (tmp, permuted_support[i]); synd[j] = fld.add (synd[j], tmp); } } //decoding polynomial loc; compute_alternant_error_locator (synd, fld, 1 << T, loc); bvector ev; if (!evaluate_error_locator_trace (loc, ev, fld) ) return 1; //couldn't decode //TODO evaluator should return error positions, not bvector. fix it everywhere! out = in; out.resize (plain_size() ); errors.clear(); errors.resize (cipher_size(), 0); //flip error positions of out. for (i = 0; i < ev.size(); ++i) if (ev[i]) { uint epos = support_pos[fld.inv (i)]; if (epos == fld.n) { //found unexpected support, die. out.clear(); return 1; } if (epos >= cipher_size() ) return 1; errors[epos] = 1; if (epos < plain_size() ) out[epos] = !out[epos]; } return 0; }
int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prng&rng) { uint i, s, t; bvector p, e, synd, synd_orig, e2; std::vector<uint> epos; permutation hpermInv; polynomial loc, Synd; s = hash_size(); if (in.size() != s) return 2; //first, prepare the codeword to canonical form for decoding Pinv.permute (in, e2); hperm.compute_inversion (hpermInv); hpermInv.permute (e2, p); //prepare extra error vector e.resize (s, 0); epos.resize (delta, 0); h.mult_vec_right (p, synd_orig); for (t = 0; t < attempts; ++t) { synd = synd_orig; for (i = 0; i < delta; ++i) { epos[i] = rng.random (s); /* we don't care about (unlikely) error bit collisions (they actually don't harm anything) */ if (!e[epos[i]]) synd.add (h[epos[i]]); e[epos[i]] = 1; } synd.to_poly (Synd, fld); compute_goppa_error_locator (Synd, fld, g, sqInv, loc); if (evaluate_error_locator_trace (loc, e2, fld) ) { //recreate the decodable codeword p.add (e); p.add (e2); hperm.permute (p, e2); //back to systematic e2.resize (signature_size() ); //strip to message Sinv.mult_vecT_left (e2, out); //signature return 0; } //if this round failed, we try a new error pattern. for (i = 0; i < delta; ++i) { //clear the errors for next cycle e[epos[i]] = 0; } } return 1; //couldn't decode }
int pubkey::verify (const bvector&in, const bvector&hash, uint delta) { bvector tmp; if (!G.mult_vecT_left (in, tmp) ) return 2; //wrong size of input if (hash.size() != tmp.size() ) return 1; //wrong size of hash, not a sig. tmp.add (hash); if (tmp.hamming_weight() > (t + delta) ) return 1; //not a signature return 0; //sig OK }
/* * we expect correct parameter size and preallocated out. Last 3 parameters are * used as a cache - just supply the same vectors everytime when you're doing * this multiple times. */ void fwht_dyadic_multiply (const bvector& a, const bvector& b, bvector& out, std::vector<int>&t, std::vector<int>&A, std::vector<int>&B) { uint i; //lift everyting to Z. for (i = 0; i < a.size(); ++i) t[i] = a[i]; fwht (t, A); for (i = 0; i < b.size(); ++i) t[i] = b[i]; fwht (t, B); //multiply diagonals to A for (i = 0; i < A.size(); ++i) A[i] *= B[i]; fwht (A, t); uint bitpos = a.size(); //no problem as a.size() == 1<<m == 2^m for (i = 0; i < t.size(); ++i) out[i] = (t[i] & bitpos) ? 1 : 0; }
int pubkey::encrypt (const bvector & in, bvector & out, const bvector&errors) { uint t = 1 << T; bvector p, g, r, cksum; uint i, j; /* * shortened checksum pair of G is computed blockwise accordingly to * the t-sized square dyadic blocks. */ //some checks if (!qd_sigs.width() ) return 1; if (qd_sigs.height() % t) return 1; if (in.size() != plain_size() ) return 2; if (errors.size() != cipher_size() ) return 2; uint blocks = qd_sigs.height() / t; cksum.resize (qd_sigs.height(), 0); p.resize (t); g.resize (t); r.resize (t); std::vector<int> c1, c2, c3; c1.resize (t); c2.resize (t); c3.resize (t); for (i = 0; i < qd_sigs.size(); ++i) { //plaintext block in.get_block (i * t, t, p); for (j = 0; j < blocks; ++j) { //checksum block qd_sigs[i].get_block (j * t, t, g); //block result fwht_dyadic_multiply (p, g, r, c1, c2, c3); cksum.add_offset (r, t * j); } } //compute ciphertext out = in; out.insert (out.end(), cksum.begin(), cksum.end() ); out.add (errors); return 0; }
int privkey::decrypt (const bvector&in, bvector&out, bvector&errors) { if (in.size() != cipher_size() ) return 2; //remove the P permutation bvector not_permuted; Pinv.permute (in, not_permuted); //prepare for decoding permutation hpermInv; //TODO pre-invert it in prepare() hperm.compute_inversion (hpermInv); bvector canonical, syndrome; hpermInv.permute (not_permuted, canonical); h.mult_vec_right (canonical, syndrome); //decode polynomial synd, loc; syndrome.to_poly (synd, fld); compute_goppa_error_locator (synd, fld, g, sqInv, loc); bvector ev; if (!evaluate_error_locator_trace (loc, ev, fld) ) return 1; //if decoding somehow failed, fail as well. //correct the errors canonical.add (ev); //shuffle back into systematic order hperm.permute (canonical, not_permuted); hperm.permute (ev, errors); //get rid of redundancy bits not_permuted.resize (plain_size() ); //unscramble the result Sinv.mult_vecT_left (not_permuted, out); return 0; }
bool evaluate_error_locator_trace (polynomial&sigma, bvector&ev, gf2m&fld) { ev.clear(); ev.resize (fld.n, 0); std::vector<polynomial> trace_aux, trace; //trace cache trace_aux.resize (fld.m); trace.resize (fld.m); trace_aux[0] = polynomial(); trace_aux[0].resize (2, 0); trace_aux[0][1] = 1; //trace_aux[0] = x trace[0] = trace_aux[0]; //trace[0] = x for (uint i = 1; i < fld.m; ++i) { trace_aux[i] = trace_aux[i - 1]; trace_aux[i].square (fld); trace_aux[i].mod (sigma, fld); trace[0].add (trace_aux[i], fld); } std::set<std::pair<uint, polynomial> > stk; //"stack" stk.insert (make_pair (0, sigma)); bool failed = false; while (!stk.empty()) { uint i = stk.begin()->first; polynomial cur = stk.begin()->second; stk.erase (stk.begin()); int deg = cur.degree(); if (deg <= 0) continue; if (deg == 1) { //found a linear factor ev[fld.mult (cur[0], fld.inv (cur[1])) ] = 1; continue; } if (i >= fld.m) { failed = true; continue; } if (trace[i].zero()) { //compute the trace if it isn't cached uint a = fld.exp (i); for (uint j = 0; j < fld.m; ++j) { trace[i].add_mult (trace_aux[j], a, fld); a = fld.mult (a, a); } } polynomial t; t = cur.gcd (trace[i], fld); polynomial q, r; cur.divmod (t, q, r, fld); stk.insert (make_pair (i + 1, t)); stk.insert (make_pair (i + 1, q)); } return !failed; }