mce_qd: decoding work, along with some fixes

- vector functions
- decoding function simplification
This commit is contained in:
Mirek Kratochvil 2012-10-22 12:10:26 +02:00
parent 5b69b38e09
commit 9620632e84
12 changed files with 169 additions and 83 deletions

View file

@ -43,17 +43,18 @@ public:
void add (const bvector&); void add (const bvector&);
void add_range (const bvector&, uint, uint); void add_range (const bvector&, uint, uint);
void add_offset (const bvector&, uint); void add_offset (const bvector&, uint);
void get_block (uint, uint, bvector&) const;
bool operator* (const bvector&); //dot product bool operator* (const bvector&); //dot product
bool zero() const; bool zero() const;
void to_poly (polynomial&, gf2m&); void to_poly (polynomial&, gf2m&) const;
void from_poly (const polynomial&, gf2m&); void from_poly (const polynomial&, gf2m&);
void to_poly_cotrace (polynomial&, gf2m&); void to_poly_cotrace (polynomial&, gf2m&) const;
void from_poly_cotrace (const polynomial&, gf2m&); void from_poly_cotrace (const polynomial&, gf2m&);
void colex_rank (bvector&); void colex_rank (bvector&) const;
void colex_unrank (bvector&, uint n, uint k); void colex_unrank (bvector&, uint n, uint k) const;
}; };
/* /*
@ -380,6 +381,7 @@ public:
gf2m fld; //we fix q=2^fld.m=fld.n, n=q/2 gf2m fld; //we fix q=2^fld.m=fld.n, n=q/2
uint T; //the QD's t parameter is 2^T. uint T; //the QD's t parameter is 2^T.
permutation block_perm; //order of blocks permutation block_perm; //order of blocks
//TODO this is derivable from hperm.
uint block_count; //blocks >= block_count are shortened-out uint block_count; //blocks >= block_count are shortened-out
permutation hperm; //block permutation of H block used to get G permutation hperm; //block permutation of H block used to get G
std::vector<uint> block_perms; //dyadic permutations of blocks std::vector<uint> block_perms; //dyadic permutations of blocks
@ -390,6 +392,8 @@ public:
polynomial g; //computed goppa polynomial polynomial g; //computed goppa polynomial
std::vector<polynomial> sqInv; std::vector<polynomial> sqInv;
std::vector<bvector> Hc; //signature lines of pre-permuted check matrix
std::vector<uint> support_pos; //pre-permuted positions of support rows
int decrypt (const bvector&, bvector&); int decrypt (const bvector&, bvector&);
int prepare(); int prepare();
@ -427,9 +431,9 @@ int generate (pubkey&, privkey&, prng&, uint m, uint T, uint b);
* Similar to Hamdi's Chained BCH Codes, but with improvement. * Similar to Hamdi's Chained BCH Codes, but with improvement.
* *
* This is experimental, unverified, probably insecure, but practical scheme * This is experimental, unverified, probably insecure, but practical scheme
* that achieves good speed, probability and key size for full decoding that is * that achieves good speed, probability and non-exponential key size for full
* needed to produce signatures. Technique is described in documentation, with * decoding that is needed to produce signatures. Technique is described in
* some (probably sufficient) notes in source code. * documentation, with some (probably sufficient) notes in source code.
* *
* Note that encryption using this scheme is impossible, as there is only an * Note that encryption using this scheme is impossible, as there is only an
* extremely tiny probability of successful decoding. * extremely tiny probability of successful decoding.

View file

@ -30,6 +30,13 @@ void bvector::add_offset (const bvector&a, uint offset)
item (offset + i) = item (offset + i) ^ a[i]; item (offset + i) = item (offset + i) ^ a[i];
} }
void bvector::get_block (uint offset, uint bs, bvector&out) const
{
if (offset + bs > size() ) return;
out.resize (bs);
for (uint i = 0; i < bs; ++i) out[i] = item (offset + i);
}
bool bvector::operator* (const bvector&a) bool bvector::operator* (const bvector&a)
{ {
bool r = 0; bool r = 0;
@ -45,7 +52,7 @@ bool bvector::zero() const
return true; return true;
} }
void bvector::to_poly (polynomial&r, gf2m&fld) void bvector::to_poly (polynomial&r, gf2m&fld) const
{ {
r.clear(); r.clear();
if (size() % fld.m) return; //impossible if (size() % fld.m) return; //impossible
@ -62,7 +69,7 @@ void bvector::from_poly (const polynomial&r, gf2m&fld)
item (i) = (r[i / fld.m] >> (i % fld.m) ) & 1; item (i) = (r[i / fld.m] >> (i % fld.m) ) & 1;
} }
void bvector::to_poly_cotrace (polynomial&r, gf2m&fld) void bvector::to_poly_cotrace (polynomial&r, gf2m&fld) const
{ {
r.clear(); r.clear();
if (size() % fld.m) return; //impossible if (size() % fld.m) return; //impossible
@ -117,7 +124,7 @@ static void combination_number (uint n, uint k, mpz_t& r)
mpz_clear (t); mpz_clear (t);
} }
static void bvector_to_mpz (bvector&v, mpz_t&r) static void bvector_to_mpz (const bvector&v, mpz_t&r)
{ {
mpz_set_ui (r, 0); mpz_set_ui (r, 0);
mpz_realloc2 (r, v.size() ); mpz_realloc2 (r, v.size() );
@ -134,7 +141,7 @@ static void mpz_to_bvector (mpz_t&x, bvector&r)
r[i] = mpz_tstbit (x, i); r[i] = mpz_tstbit (x, i);
} }
void bvector::colex_rank (bvector&r) void bvector::colex_rank (bvector&r) const
{ {
mpz_t res, t, t2; mpz_t res, t, t2;
mpz_init_set_ui (res, 0); mpz_init_set_ui (res, 0);
@ -158,8 +165,7 @@ void bvector::colex_rank (bvector&r)
mpz_clear (res); mpz_clear (res);
} }
#include <stdio.h> void bvector::colex_unrank (bvector&res, uint n, uint k) const
void bvector::colex_unrank (bvector&res, uint n, uint k)
{ {
mpz_t r, t, t2; mpz_t r, t, t2;
mpz_init (r); mpz_init (r);

View file

@ -1,7 +1,7 @@
#include "decoding.h" #include "decoding.h"
void compute_error_locator (bvector&syndrome, gf2m&fld, polynomial& goppa, void compute_error_locator (polynomial&syndrome, gf2m&fld, polynomial& goppa,
std::vector<polynomial>& sqInv, polynomial&out) std::vector<polynomial>& sqInv, polynomial&out)
{ {
if (syndrome.zero() ) { if (syndrome.zero() ) {
@ -11,9 +11,7 @@ void compute_error_locator (bvector&syndrome, gf2m&fld, polynomial& goppa,
return; return;
} }
polynomial v; polynomial v = syndrome;
syndrome.to_poly (v, fld);
v.inv (goppa, fld); // v=Synd^-1 mod goppa v.inv (goppa, fld); // v=Synd^-1 mod goppa
if (v.size() < 2) v.resize (2, 0); if (v.size() < 2) v.resize (2, 0);

View file

@ -6,7 +6,7 @@
using namespace ccr; using namespace ccr;
void compute_error_locator (bvector&syndrome, void compute_error_locator (polynomial&syndrome,
gf2m&fld, gf2m&fld,
polynomial&goppa, polynomial&goppa,
std::vector<polynomial>& sqInv, std::vector<polynomial>& sqInv,

View file

@ -233,6 +233,7 @@ bool matrix::create_goppa_generator (matrix&g, const permutation&p)
t.get_right_square (sinv); t.get_right_square (sinv);
if (!sinv.compute_inversion (s) ) return false; //meant to be retried. if (!sinv.compute_inversion (s) ) return false; //meant to be retried.
//TODO why multiply and THEN strip?
s.mult (t); s.mult (t);
s.strip_right_square (t); //matrix pingpong for the result s.strip_right_square (t); //matrix pingpong for the result
t.compute_transpose (s); t.compute_transpose (s);

View file

@ -78,8 +78,9 @@ int privkey::decrypt (const bvector&in, bvector&out)
h.mult_vec_right (canonical, syndrome); h.mult_vec_right (canonical, syndrome);
//decode //decode
polynomial loc; polynomial synd, loc;
compute_error_locator (syndrome, fld, g, sqInv, loc); syndrome.to_poly (synd, fld);
compute_error_locator (synd, fld, g, sqInv, loc);
bvector ev; bvector ev;
if (!evaluate_error_locator_trace (loc, ev, fld) ) if (!evaluate_error_locator_trace (loc, ev, fld) )
@ -113,7 +114,7 @@ int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prn
bvector p, e, synd, synd_orig, e2; bvector p, e, synd, synd_orig, e2;
std::vector<uint> epos; std::vector<uint> epos;
permutation hpermInv; permutation hpermInv;
polynomial loc; polynomial loc, Synd;
s = hash_size(); s = hash_size();
@ -142,7 +143,8 @@ int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prn
e[epos[i]] = 1; e[epos[i]] = 1;
} }
compute_error_locator (synd, fld, g, sqInv, loc); synd.to_poly (Synd, fld);
compute_error_locator (Synd, fld, g, sqInv, loc);
if (evaluate_error_locator_trace (loc, e2, fld) ) { if (evaluate_error_locator_trace (loc, e2, fld) ) {

View file

@ -74,7 +74,7 @@ int privkey::sign (const bvector&in, bvector&out,
bvector e, e2, synd, synd_orig, cw, cwc, plain, overlap; bvector e, e2, synd, synd_orig, cw, cwc, plain, overlap;
std::vector<uint> epos; std::vector<uint> epos;
permutation hpermInv; permutation hpermInv;
polynomial loc; polynomial loc, Synd;
uint i, t; uint i, t;
uint mt = fld.m * codes[0].g.degree(), uint mt = fld.m * codes[0].g.degree(),
@ -123,7 +123,8 @@ int privkey::sign (const bvector&in, bvector&out,
e[epos[i]] = 1; e[epos[i]] = 1;
} }
compute_error_locator (synd, fld, synd.to_poly (Synd, fld);
compute_error_locator (Synd, fld,
codes[ci].g, codes[ci].g,
codes[ci].sqInv, loc); codes[ci].sqInv, loc);

View file

@ -5,6 +5,7 @@ using namespace ccr;
using namespace ccr::mce_qd; using namespace ccr::mce_qd;
#include "decoding.h" #include "decoding.h"
#include "fwht.h"
#include <set> #include <set>
@ -37,6 +38,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
priv.T = T; priv.T = T;
uint t = 1 << T; uint t = 1 << T;
std::cout << "generate" << std::endl;
//convenience //convenience
gf2m&fld = priv.fld; gf2m&fld = priv.fld;
std::vector<uint>&Hsig = priv.Hsig; std::vector<uint>&Hsig = priv.Hsig;
@ -56,6 +58,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
std::set<uint> used; std::set<uint> used;
used.clear(); used.clear();
std::cout << "attempt..." << std::endl;
//first off, compute the H signature //first off, compute the H signature
Hsig[0] = choose_random (fld.n, rng, used); Hsig[0] = choose_random (fld.n, rng, used);
@ -87,6 +90,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
//from now on, we fix 'omega' from the paper to zero. //from now on, we fix 'omega' from the paper to zero.
std::cout << "goppa..." << std::endl;
//assemble goppa polynomial. //assemble goppa polynomial.
g.clear(); g.clear();
g.resize (1, 1); //g(x)=1 so we can multiply it g.resize (1, 1); //g(x)=1 so we can multiply it
@ -96,8 +100,12 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
//tmp(x)=x-z=x-(1/h_i) //tmp(x)=x-z=x-(1/h_i)
tmp[0] = fld.inv (Hsig[i]); tmp[0] = fld.inv (Hsig[i]);
g.mult (tmp, fld); g.mult (tmp, fld);
std::cout << "computing g... " << g;
} }
std::cout << "Goppa poly " << g;
std::cout << "support..." << std::endl;
//compute the support, retry if it has two equal elements. //compute the support, retry if it has two equal elements.
used.clear(); used.clear();
bool consistent = true; bool consistent = true;
@ -112,15 +120,18 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
} }
if (g.eval (support[i], fld) == 0) { if (g.eval (support[i], fld) == 0) {
std::cout << "support zero!" << std::endl;
consistent = false; consistent = false;
break; break;
} }
std::cout << "support at " << i << ": " << support[i] << std::endl;
used.insert (support[i]); used.insert (support[i]);
} }
if (!consistent) continue; //retry if (!consistent) continue; //retry
std::cout << "blocks..." << std::endl;
//now the blocks. //now the blocks.
uint block_size = 1 << T, uint block_size = 1 << T,
h_block_count = (fld.n / 2) / block_size; h_block_count = (fld.n / 2) / block_size;
@ -135,13 +146,16 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
(Hsig.begin() + i * block_size, (Hsig.begin() + i * block_size,
Hsig.begin() + (i + 1) * block_size); Hsig.begin() + (i + 1) * block_size);
std::cout << "permuting blocks..." << std::endl;
//permute them //permute them
priv.block_perm.generate_random (h_block_count, rng); priv.block_perm.generate_random (h_block_count, rng);
priv.block_perm.permute (bl, blp); priv.block_perm.permute (bl, blp);
std::cout << "discarding blocks..." << std::endl;
//discard blocks //discard blocks
blp.resize (block_count); blp.resize (block_count);
std::cout << "permuting dyadic blocks..." << std::endl;
//permute individual blocks //permute individual blocks
priv.block_perms.resize (block_count); priv.block_perms.resize (block_count);
bl.resize (blp.size() ); bl.resize (blp.size() );
@ -158,7 +172,10 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
matrix r, ri, l; matrix r, ri, l;
for (;;) { //try several permutations to construct G
uint attempts = 0;
for (attempts = 0; attempts < block_count; ++attempts) {
std::cout << "generating G..." << std::endl;
priv.hperm.generate_random (block_count, rng); priv.hperm.generate_random (block_count, rng);
for (uint i = 0; i < block_count; ++i) for (uint i = 0; i < block_count; ++i)
@ -177,18 +194,25 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
*/ */
Hc.get_right_square (r); Hc.get_right_square (r);
std::cout << "RIGHT SQUARE " << r;
if (!r.compute_inversion (ri) ) if (!r.compute_inversion (ri) )
continue; //retry with other hperm continue; //retry with other code
std::cout << "Rinv " << ri;
Hc.strip_right_square (l); Hc.strip_right_square (l);
ri.mult (l); ri.mult (l);
std::cout << "l " << ri;
break; break;
} }
if (attempts == block_count) //generating G failed, retry all
continue;
/* /*
* Redundancy-checking part of G is now (transposed) in ri. * Redundancy-checking part of G is now (transposed) in ri.
* Get QD signatures by getting every t'th row (transposed). * Get QD signatures by getting every t'th row (transposed).
*/ */
std::cout << "pubkey..." << std::endl;
pub.T = T; pub.T = T;
pub.qd_sigs.resize (ri.width() / t); pub.qd_sigs.resize (ri.width() / t);
for (uint i = 0; i < ri.width(); i += t) for (uint i = 0; i < ri.width(); i += t)
@ -200,13 +224,13 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
int privkey::prepare() int privkey::prepare()
{ {
std::cout << "prepare" << std::endl;
//compute H signature from essence //compute H signature from essence
Hsig.resize (fld.n / 2); Hsig.resize (fld.n / 2);
Hsig[0] = fld.inv (essence[fld.m - 1]); Hsig[0] = fld.inv (essence[fld.m - 1]);
for (uint s = 0; s < fld.m - 1; ++s) { for (uint s = 0; s < fld.m - 1; ++s) {
uint i = 1 << s; //i = 2^s uint i = 1 << s; //i = 2^s
//TODO verify this
Hsig[i] = fld.inv (fld.add (essence[s], essence[fld.m - 1]) ); Hsig[i] = fld.inv (fld.add (essence[s], essence[fld.m - 1]) );
for (uint j = 1; j < i; ++j) for (uint j = 1; j < i; ++j)
@ -228,6 +252,11 @@ int privkey::prepare()
} }
//TODO prepare permuted Hsig (that can be applied to the ciphertext)
//TODO prepare function that converts a support zero to ciphertext
//position
//goppa polynomial //goppa polynomial
g.clear(); g.clear();
g.resize (1, 1); g.resize (1, 1);
@ -245,12 +274,11 @@ int privkey::prepare()
return 0; return 0;
} }
#include "fwht.h"
int pubkey::encrypt (const bvector& in, bvector&out, prng&rng) int pubkey::encrypt (const bvector& in, bvector&out, prng&rng)
{ {
uint t = 1 << T; uint t = 1 << T;
bvector p, g, r, cksum; bvector p, g, r, cksum;
uint i, j, k;
/* /*
* shortened checksum pair of G is computed blockwise accordingly to * shortened checksum pair of G is computed blockwise accordingly to
@ -268,17 +296,23 @@ int pubkey::encrypt (const bvector& in, bvector&out, prng&rng)
g.resize (t); g.resize (t);
r.resize (t); r.resize (t);
for (uint i = 0; i < qd_sigs.size(); ++i) { for (i = 0; i < qd_sigs.size(); ++i) {
//plaintext block std::cout << "Signature line " << i << ": " << qd_sigs[i];
for (uint k = 0; k < t; ++k) p[k] = in[k+i*t]; }
for (uint j = 0; j < blocks; ++j) { 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 //checksum block
for (uint k = 0; k < t; ++k) g[k] = qd_sigs[i][k+j*t]; qd_sigs[i].get_block (j * t, t, g);
//block result //block result
fwht_dyadic_multiply (p, g, r); fwht_dyadic_multiply (p, g, r);
//std::cout << "DYADIC MULTIPLY: " << p << g << r << "---" << std::endl;
cksum.add_offset (r, t * j); cksum.add_offset (r, t * j);
//std::cout << "CKSUM NOW: " << cksum;
} }
} }
@ -296,6 +330,7 @@ int pubkey::encrypt (const bvector& in, bvector&out, prng&rng)
//compute ciphertext //compute ciphertext
out = in; out = in;
out.insert (out.end(), cksum.begin(), cksum.end() ); out.insert (out.end(), cksum.begin(), cksum.end() );
std::cout << "without errors: " << out;
out.add (e); out.add (e);
return 0; return 0;
@ -303,7 +338,45 @@ int pubkey::encrypt (const bvector& in, bvector&out, prng&rng)
int privkey::decrypt (const bvector&in, bvector&out) int privkey::decrypt (const bvector&in, bvector&out)
{ {
//TODO decoding if (in.size() != cipher_size() ) return 2;
//multiply line-by-line block-by-block by H
uint block_size = 1 << T;
bvector synd_vec;
bvector hp, cp, res;
uint i, j, k;
synd_vec.resize (block_size * fld.m);
hp.resize (block_size);
cp.resize (block_size);
res.resize (block_size);
for (i = 0; i < block_count; ++i) {
in.get_block (i * block_size, block_size, cp);
for (j = 0; j < fld.m; ++j) {
Hc[j].get_block (i * block_size, block_size, hp);
fwht_dyadic_multiply (hp, cp, res);
synd_vec.add_offset (res, j * block_size);
}
}
//decoding
polynomial synd, loc;
synd_vec.to_poly_cotrace (synd, fld);
compute_error_locator (synd, fld, g, sqInv, 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;
//flip error positions of out.
for (i = 0; i < ev.size(); ++i) if (ev[i]) {
if (support_pos[i] == -1) return 1; //couldn't decode TODO is it true?
out[i] = !out[i];
}
return 0; return 0;
} }

View file

@ -57,8 +57,9 @@ int privkey::decrypt (const bvector&in, bvector&out)
bvector unsc; //unscrambled bvector unsc; //unscrambled
Sinv.mult_vec_right (in, unsc); Sinv.mult_vec_right (in, unsc);
polynomial loc; polynomial loc, synd;
compute_error_locator (unsc, fld, g, sqInv, loc); unsc.to_poly (synd, fld);
compute_error_locator (synd, fld, g, sqInv, loc);
bvector ev; bvector ev;
if (!evaluate_error_locator_trace (loc, ev, fld) ) if (!evaluate_error_locator_trace (loc, ev, fld) )
@ -76,8 +77,7 @@ int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prn
uint i, s, t; uint i, s, t;
bvector synd_unsc, synd, e; bvector synd_unsc, synd, e;
polynomial loc, Synd;
polynomial loc;
s = hash_size(); s = hash_size();
if (in.size() != s) return 2; if (in.size() != s) return 2;
@ -92,7 +92,8 @@ int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prn
Sinv.mult_vec_right (synd, synd_unsc); Sinv.mult_vec_right (synd, synd_unsc);
compute_error_locator (synd_unsc, fld, g, sqInv, loc); synd_unsc.to_poly (Synd, fld);
compute_error_locator (Synd, fld, g, sqInv, loc);
if (evaluate_error_locator_trace (loc, e, fld) ) { if (evaluate_error_locator_trace (loc, e, fld) ) {