From fb5405633180cd89f3fc1c35b05e73c9d2d30c5b Mon Sep 17 00:00:00 2001
From: Mirek Kratochvil <exa.exa@gmail.com>
Date: Sun, 2 Dec 2012 12:50:29 +0100
Subject: [PATCH] serialization and cfs_qd work

---
 include/codecrypt.h   |  97 ++++++-
 lib/cfs_qd.cpp        | 580 ++++++++++++++++++++++++++++++++++++++++++
 lib/decoding.cpp      |   2 +-
 lib/mce_qd.cpp        |  61 ++---
 lib/qd_utils.cpp      |  12 +
 lib/qd_utils.h        |   4 +
 lib/serialization.cpp | 101 +++++---
 7 files changed, 775 insertions(+), 82 deletions(-)
 create mode 100644 lib/cfs_qd.cpp

diff --git a/include/codecrypt.h b/include/codecrypt.h
index 2f4beb1..48c8168 100644
--- a/include/codecrypt.h
+++ b/include/codecrypt.h
@@ -207,6 +207,11 @@ public:
 	void compute_inversion (permutation&) const;
 
 	void generate_random (uint n, prng&);
+	void generate_identity (uint n) {
+		resize (n);
+		for (uint i = 0; i < n; ++i)
+			item (i) = i;
+	}
 
 	//TODO permute_inv is easy, do it everywhere
 	template<class A, class R> void permute (const A&a, R&r) const {
@@ -425,6 +430,9 @@ public:
 	uint signature_size() {
 		return plain_size();
 	}
+	uint signature_weight() {
+		return plain_weight();
+	}
 
 	sencode* serialize();
 	bool unserialize (sencode*);
@@ -454,6 +462,9 @@ public:
 	uint signature_size() {
 		return plain_size();
 	}
+	uint signature_weight() {
+		return plain_weight();
+	}
 
 	sencode* serialize();
 	bool unserialize (sencode*);
@@ -480,14 +491,10 @@ public:
 	gf2m fld;   //we fix q=2^fld.m=fld.n, n=q/2
 	uint T;     //the QD's t parameter is 2^T.
 	permutation block_perm; //order of blocks
-	permutation hperm; //block permutation of H block used to get G
 	std::vector<uint> block_perms; //dyadic permutations of blocks
+	permutation hperm; //block permutation of H block used to get G
 
 	//derivable stuff
-	std::vector<uint> Hsig; //signature of canonical H matrix
-	std::vector<uint> support; //computed goppa support
-	uint omega;
-
 	//cols of check matrix of g^2(x)
 	std::vector<polynomial> Hc;
 	//pre-permuted positions of support rows
@@ -497,10 +504,10 @@ public:
 	int prepare();
 
 	uint cipher_size() {
-		return (1 << T) * block_count;
+		return (1 << T) * hperm.size();
 	}
 	uint plain_size() {
-		return (1 << T) * (block_count - fld.m);
+		return (1 << T) * (hperm.size() - fld.m);
 	}
 
 	sencode* serialize();
@@ -511,7 +518,7 @@ class pubkey
 {
 public:
 	uint T;
-	std::vector<bvector> qd_sigs;
+	matrix qd_sigs;
 
 	int encrypt (const bvector&, bvector&, prng&);
 
@@ -521,15 +528,87 @@ public:
 	uint plain_size() {
 		return (1 << T) * qd_sigs.size();
 	}
+
+	sencode* serialize();
+	bool unserialize (sencode*);
 };
 
 int generate (pubkey&, privkey&, prng&, uint m, uint T, uint b);
 }
 
+/*
+ * QD-CFS
+ *
+ * according to "Quasi-dyadic CFS signatures" by Baretto, Cayrel, Misoczki,
+ * Niebuhr.
+ *
+ * As always with Niederreiter, hash must be of weight t (=1<<T)
+ */
+namespace cfs_qd
+{
+class privkey
+{
+public:
+	std::vector<uint> essence;
+	gf2m fld; //we fix q=2^fld.m=fld.n, n=q/2
+	uint T, t; //size of blocks is 1<<T, t is error correction capability
+	permutation block_perm; //order of blocks
+	std::vector<uint> block_perms; //dyadic permutations of blocks
+
+	//derivable stuff
+	polynomial g; //goppa
+	std::vector<polynomial> sqInv; //sqroot mod g
+	//pre-permuted positions of support rows
+	std::vector<uint> support_pos;
+	std::vector<polynomial> syndS;
+
+	int sign (const bvector&, bvector&, uint d, uint attempts, prng&);
+	int prepare();
+
+	uint hash_size() {
+		return t * fld.m;
+	}
+	uint signature_size() {
+		return (1 << T) * block_perms.size();
+	}
+	uint signature_weight() {
+		return t;
+	}
+
+	sencode* serialize();
+	bool unserialize (sencode*);
+};
+
+class pubkey
+{
+public:
+	uint t, T;
+	//cols of H
+	std::vector<bvector> qd_sigs;
+
+	int verify (const bvector&, const bvector&, uint);
+
+	uint hash_size() {
+		return t * qd_sigs.size();
+	}
+	uint signature_size() {
+		return qd_sigs[0].size();
+	}
+	uint signature_weight() {
+		return t;
+	}
+
+	sencode* serialize();
+	bool unserialize (sencode*);
+};
+
+int generate (pubkey&, privkey&, prng&, uint m, uint T, uint t, uint b);
+}
+
 /*
  * McEliece on Overlapping Chain of Goppa Codes
  *
- * Similar to Hamdi's Chained BCH Codes, but with improvement.
+ * Similar to Hamdi's Chained BCH Codes, but with improvements.
  *
  * This is experimental, unverified, probably insecure, but practical scheme
  * that achieves good speed, probability and non-exponential key size for full
diff --git a/lib/cfs_qd.cpp b/lib/cfs_qd.cpp
new file mode 100644
index 0000000..0989011
--- /dev/null
+++ b/lib/cfs_qd.cpp
@@ -0,0 +1,580 @@
+
+/*
+ * This file is part of Codecrypt.
+ *
+ * Codecrypt is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Codecrypt is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Codecrypt. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "codecrypt.h"
+
+using namespace ccr;
+using namespace ccr::cfs_qd;
+
+#include "decoding.h"
+#include "qd_utils.h"
+
+#include <set>
+
+int cfs_qd::generate (pubkey&pub, privkey&priv, prng&rng,
+                      uint m, uint T, uint t, uint block_discard)
+{
+	priv.fld.create (m);
+	priv.T = T;
+	uint block_size = 1 << T;
+	if (t > block_size) return 2;
+	priv.t = t;
+
+	//convenience
+	gf2m&fld = priv.fld;
+	std::vector<uint>&essence = priv.essence;
+
+	std::vector<uint> support, Hsig;
+	polynomial g;
+	uint i, j;
+
+	//prepare for data
+	Hsig.resize (fld.n);
+	support.resize (fld.n);
+	essence.resize (m + 1);
+	//note that q=2^m, algo. n=q/2, log n = m-1
+
+	//retry generating until goppa code is produced.
+	for (;;) {
+
+		std::cout << "attempt" << std::endl;
+
+		std::set<uint> used;
+		used.clear();
+
+		//first off, compute the H signature
+
+		Hsig[0] = choose_random (fld.n, rng, used);
+		essence[m] = fld.inv (Hsig[0]);
+		//essence[m] is now used as precomputed 1/h_0
+
+		for (uint s = 0; s < m; ++s) {
+			i = 1 << s; //i = 2^s
+
+			Hsig[i] = choose_random (fld.n, rng, used);
+			essence[s] = fld.add (essence[m], fld.inv (Hsig[i]) );
+			used.insert (fld.inv (essence[s]) );
+
+			for (j = 1; j < i; ++j) {
+				uint hij = fld.inv
+				           (fld.add
+				            (fld.inv (Hsig[i]),
+				             fld.add (
+				                 fld.inv (Hsig[j]),
+				                 essence[m]
+				             ) ) );
+				if ( (!Hsig[i]) || (!Hsig[j]) ) hij = 0;
+				Hsig[i + j] = hij;
+				if (hij) {
+					used.insert (Hsig[i + j]);
+					/*used.insert (fld.inv
+					             (fld.add
+					              (fld.inv (Hsig[i + j]),
+					               essence[m]) ) );*/
+				}
+			}
+		}
+
+		std::cout << "Gen Hsig: ";
+		for (i = 0; i < fld.n; ++i) std::cout << Hsig[i] << ' ';
+		std::cout << std::endl;
+
+		//let's play with blocks.
+		uint block_size = 1 << T,
+		     h_block_count = fld.n / block_size,
+		     block_count = h_block_count - block_discard;
+
+		//check if we have enough good blocks.
+		std::vector<bool> block_status;
+		uint badblocks;
+		block_status.resize (h_block_count);
+
+		badblocks = 0;
+		for (i = 0; i < h_block_count; ++i) {
+			block_status[i] = true;
+			for (j = 0; j < block_size; ++j)
+				if (!Hsig[i * block_size + j]) {
+					block_status[i] = false;
+					break;
+				}
+			if (!block_status[i]) ++badblocks;
+		}
+
+		std::cout << "badblocks: " << badblocks << std::endl;
+
+		if (badblocks > block_discard) continue; //don't have enough good blocks
+		if (!block_status[0]) continue; //cannot assemble goppa poly
+
+		std::cout << "lol contd." << std::endl;
+
+		//reconstruct g
+		used.clear();
+		g.clear();
+		g.resize (1, 1); //g(x)=1 so we can multiply it
+		polynomial tmp;
+		tmp.resize (2, 1); //tmp(x)=x-1
+		bool consistent = true;
+		for (i = 0; i < t; ++i) {
+			//tmp(x)=x-z=x-(1/h_i) where h_i is squared!
+			tmp[0] = fld.inv (Hsig[i]);
+			if (used.count (tmp[0]) ) {
+				consistent = false;
+				break;
+			}
+			used.insert (tmp[0]);
+			g.mult (tmp, fld);
+		}
+		if (!consistent) continue; //retry
+
+		std::cout << "lol have g: " << g;
+
+		//compute the support, retry if it has two equal elements.
+		for (i = 0; i < fld.n; ++i) {
+			if (!block_status[i / block_size]) continue;
+			support[i] = fld.add (
+			                 fld.inv (Hsig[i]),
+			                 essence[m]);
+
+			std::cout << "support " << i << " = " << support[i] << std::endl;
+			if (used.count (support[i]) ) {
+				std::cout << "support inconsistent at " << i << std::endl;
+				++badblocks;
+				block_status[i / block_size] = false;
+				break;
+			}
+
+			used.insert (support[i]);
+		}
+
+		std::cout << "bad: " << badblocks << std::endl;
+		if (badblocks > block_discard) continue;
+
+		//assemble blocks to bl
+		std::vector<polynomial> bl, blp;
+		bl.resize (h_block_count);
+		for (i = 0; i < h_block_count; ++i) {
+			bl[i].resize (block_size);
+			for (j = 0; j < block_size; ++j)
+				bl[i][j] = Hsig[i * block_size + j];
+		}
+
+		//permute the blocks. first move the damaged to discard area
+		priv.block_perm.generate_identity (h_block_count);
+		uint oks = h_block_count;
+		for (i = 0; i < oks; ++i)
+			if (!block_status[i]) {
+				std::cout << "removing one" << std::endl;
+				--oks;
+				priv.block_perm[i] = oks;
+				priv.block_perm[oks] = i;
+				//swap block statuses as well
+				bool tmp = block_status[i];
+				block_status[i] = block_status[oks];
+				block_status[oks] = tmp;
+				--i;
+			}
+		std::cout << "BLOCK " << priv.block_perm;
+		permutation rest_perm;
+		rest_perm.generate_random (oks, rng);
+		//permute the undamaged part of block_perm by hand TODO FIXME
+		//for (i = 0; i < oks; ++i) rest_perm[i] = priv.block_perm[rest_perm[i]];
+		//for (i = 0; i < oks; ++i) priv.block_perm[i] = rest_perm[i];
+
+		//now we can safely permute and discard blocks
+		priv.block_perm.permute (bl, blp);
+		blp.resize (block_count);
+
+		//permute individual blocks
+		priv.block_perms.resize (block_count);
+		bl.resize (blp.size() );
+		for (i = 0; i < block_count; ++i) {
+			priv.block_perms[i] = rng.random (block_size);
+			permutation::permute_dyadic (priv.block_perms[i],
+			                             blp[i], bl[i]);
+		}
+
+		//construct H
+		pub.qd_sigs.resize (fld.m);
+		bvector col;
+		bvector block;
+		for (i = 0; i < fld.m; ++i)
+			pub.qd_sigs[i].resize (block_count * block_size);
+		for (i = 0; i < block_count; ++i) {
+			col.from_poly_cotrace (bl[i], fld);
+			for (j = 0; j < fld.m; ++j) {
+				col.get_block (j * block_size,
+				               block_size, block);
+
+				pub.qd_sigs[j].set_block
+				(block, block_size * i);
+			}
+		}
+
+		//finish the pubkey
+		pub.T = T;
+		pub.t = t;
+
+		return 0;
+	}
+}
+
+int privkey::prepare()
+{
+	uint s, i, j, k;
+	std::vector<uint> Hsig, support;
+	uint omega;
+
+	uint block_count = block_perms.size(),
+	     block_size = 1 << T;
+
+	//compute H signature from essence
+	Hsig.resize (fld.n);
+	Hsig[0] = fld.inv (essence[fld.m]);
+	for (s = 0; s < fld.m; ++s) {
+		i = 1 << s; //i = 2^s
+
+		Hsig[i] = fld.inv (fld.add (essence[s], essence[fld.m]) );
+
+		for (j = 1; j < i; ++j)
+			Hsig[i + j] = fld.inv
+			              (fld.add
+			               (fld.inv (Hsig[i]),
+			                fld.add (
+			                    fld.inv (Hsig[j]),
+			                    essence[fld.m]
+			                ) ) );
+	}
+	std::cout << "Gen Hsig: ";
+	for (i = 0; i < fld.n; ++i) std::cout << Hsig[i] << ' ';
+	std::cout << std::endl;
+
+
+	//goppa polynomial with omega=0
+	std::set<uint> used;
+	used.clear();
+
+	polynomial tmp;
+	g.clear();
+	g.resize (1, 1); //g(x)=1
+	tmp.clear();
+	tmp.resize (2, 1); //tmp(x)=x+1
+	for (i = 0; i < t; ++i) {
+		tmp[0] = fld.inv (Hsig[i]); //tmp(x)=x+1/h_i
+		if (used.count (tmp[0]) )
+			return 1;
+		std::cout << tmp[0] << std::endl;
+		used.insert (tmp[0]);
+		g.mult (tmp, fld);
+	}
+
+	std::cout << "HERE 1" << std::endl;
+	//compute the support with omega=0
+	support.resize (fld.n);
+	for (i = 0; i < fld.n; ++i) {
+		//don't compute with discarded support
+		if (block_perm[i / block_size] >= block_count) continue;
+		support[i] = fld.add
+		             (fld.inv (Hsig[i]),
+		              essence[fld.m]);
+		std::cout << "support " << i << " = " << support[i] << std::endl;
+		if (used.count (support[i]) ) //invalid support
+			return 1;
+		used.insert (support[i]);
+	}
+
+	std::cout << "HERE LOLOLOLOLOL" << std::endl;
+	//choose omega
+	omega = fld.n;
+	for (i = 0; i < fld.n; ++i)
+		if (!used.count (i) ) {
+			omega = i;
+			break;
+		}
+	if (omega == fld.n) return 1;
+
+	//modify support to omega-ized version
+	for (i = 0; i < support.size(); ++i)
+		support[i] = fld.add (support[i], omega);
+
+	//modify g to omega-ized version
+	g.clear();
+	tmp.clear();
+	g.resize (1, 1); //g(x)=1
+	tmp.resize (2, 1); //tmp(x)=x+1
+	for (i = 0; i < t; ++i) {
+		tmp[0] = fld.add (fld.inv (Hsig[i]), omega);
+		g.mult (tmp, fld);
+	}
+
+	g.compute_square_root_matrix (sqInv, fld);
+
+	// prepare permuted support, from that prepare permuted check matrix
+	// (so that it can be applied directly)
+	uint pos;
+	std::vector<uint> sbl1, sbl2, permuted_support;
+
+	sbl1.resize (block_size);
+	sbl2.resize (block_size);
+	permuted_support.resize (block_size * block_count);
+
+	//permute support
+	for (i = 0; i < fld.n / block_size; ++i) {
+		pos = block_perm[i];
+		if (pos >= block_count) continue; //was discarded
+
+		//permute i-th block of support
+		for (j = 0; j < block_size; ++j)
+			sbl1[j] = support[j + i * block_size];
+
+		permutation::permute_dyadic (block_perms[pos], sbl1, sbl2);
+
+		//store support to permuted support
+		for (j = 0; j < block_size; ++j)
+			permuted_support[j + pos * block_size] = sbl2[j];
+	}
+
+	//convert the permuted support to actual lookup
+	support_pos.clear();
+	//fld.n in support lookup means that it isn't there (we don't have -1)
+	support_pos.resize (fld.n, fld.n);
+	for (i = 0; i < block_size * block_count; ++i)
+		support_pos[permuted_support[i]] = i;
+
+	/*
+	 * TODO move this to separate function
+	 *
+	 * prepare the matrix to compute decodable syndrome from QD matrix. From Barreto's slides:
+	 *
+	 * A is public check matrix
+	 * H is private check matrix producing decodable syndromes
+	 *
+	 * H=SA for some S
+	 * therefore if
+	 *
+	 * synd = A * codeword
+	 *
+	 * then
+	 *
+	 * S*synd = H*codeword
+	 *
+	 * and S = H * A^T * (A * A^T)^-1
+	 */
+
+	std::vector<std::vector<uint> > ma, mb, tmpa, tmph;
+	std::vector<uint> t1, t2;
+
+	/*
+	 * First, precompute the matrices A and H
+	 */
+
+	tmpa.resize (t);
+	tmph.resize (t);
+	for (i = 0; i < t; ++i) {
+		tmpa[i].resize (fld.n);
+		tmph[i].resize (fld.n);
+	}
+
+	for (i = 0; i < t; ++i)
+		permutation::permute_dyadic (i, Hsig, tmpa[i]);
+
+	std::cout << "TMPA" << std::endl;
+	for (i = 0; i < t; ++i) {
+		for (j = 0; j < fld.n; ++j) std::cout << tmpa[i][j] << ' ';
+		std::cout << std::endl;
+	}
+
+
+	polynomial tmpcol;
+	for (i = 0; i < fld.n; ++i) {
+		tmpcol.resize (2);
+		tmpcol[0] = support[i];
+		tmpcol[1] = 1;
+		tmpcol.inv (g, fld);
+		tmpcol.resize (t, 0);
+		for (j = 0; j < t; ++j) tmph[j][i] = tmpcol[j];
+	}
+
+	/*
+	 * compute H * H^T to ma and A * H^T to mb.
+	 */
+
+	ma.resize (t);
+	mb.resize (t);
+	for (i = 0; i < t; ++i) {
+		ma[i].resize (t, 0);
+		mb[i].resize (t, 0);
+	}
+
+	for (i = 0; i < t; ++i) for (j = 0; j < t; ++j) {
+			for (k = 0; k < fld.n; ++k) {
+				ma[i][j] = fld.add (ma[i][j], fld.mult (tmph[i][k], tmph[j][k]) );
+				mb[i][j] = fld.add (mb[i][j], fld.mult (tmpa[i][k], tmph[j][k]) );
+			}
+		}
+
+	std::cout << "MA" << std::endl;
+	for (i = 0; i < t; ++i) {
+		for (j = 0; j < t; ++j) std::cout << ma[i][j] << ' ';
+		std::cout << std::endl;
+	}
+
+	std::cout << "MB" << std::endl;
+	for (i = 0; i < t; ++i) {
+		for (j = 0; j < t; ++j) std::cout << mb[i][j] << ' ';
+		std::cout << std::endl;
+	}
+
+	/*
+	 * now invert mb into ma as (mb|ma) to (I|ma*mb^-1)
+	 *
+	 * (result will be transposed, but that's actually good for our purpose)
+	 */
+
+	uint x;
+	//gauss step
+	for (i = 0; i < t; ++i) {
+		//find pivot
+		for (j = i; j < t; ++j) if (mb[j][i] != 0) break;
+		if (j >= t) return 1; //no pivot -> not invertible
+		if (j > i) {
+			ma[j].swap (ma[i]);
+			mb[j].swap (mb[i]);
+		}
+		//normalize
+		x = fld.inv (mb[i][i]);
+		for (j = 0; j < t; ++j) {
+			ma[i][j] = fld.mult (ma[i][j], x);
+			mb[i][j] = fld.mult (mb[i][j], x);
+		}
+		//zero rows below
+		for (j = i + 1; j < t; ++j) {
+			x = mb[j][i];
+			if (x == 0) continue;
+			for (k = 0; k < t; ++k) {
+				ma[j][k] = fld.add (ma[j][k], fld.mult (x, ma[i][k]) );
+				mb[j][k] = fld.add (mb[j][k], fld.mult (x, mb[i][k]) );
+			}
+		}
+	}
+
+	//jordan step
+	std::cout << "jordan step..." << std::endl;
+	for (i = 0; i < t; ++i) {
+		for (j = i + 1; j < t; ++j) {
+			x = mb[t - j - 1][t - i - 1];
+			if (x == 0) continue;
+			for (k = 0; k < t; ++k) {
+				ma[t - j - 1][k] = fld.add (ma[t - j - 1][k], fld.mult (x, ma[t - i - 1][k]) );
+				mb[t - j - 1][k] = fld.add (mb[t - j - 1][k], fld.mult (x, mb[t - i - 1][k]) );
+			}
+		}
+	}
+
+	//result is now transposed in ma.
+	syndS.resize (t);
+	for (i = 0; i < t; ++i) {
+		syndS[i].resize (t);
+		for (j = 0; j < t; ++j) syndS[i][j] = ma[i][j];
+	}
+
+	std::cout << "SyndS is OKAY!" << std::endl;
+
+	polynomial decsynd, loc;
+	for (i = 0; i < t; ++i)
+		decsynd.add_mult (syndS[i], Hsig[i], fld);
+	compute_goppa_error_locator (decsynd, fld, g, sqInv, loc);
+	std::cout << "TEST LOCATOR: " << loc;
+
+	return 0;
+}
+
+int privkey::sign (const bvector& hash, bvector&signature,
+                   uint delta, uint attempts, prng&rng)
+{
+	if (hash.size() != hash_size() ) return 2;
+
+	polynomial synd, decsynd, tmp, loc;
+	bvector ev, h2;
+
+	uint i;
+
+	for (uint att = 0; att < attempts; ++att) {
+		h2 = hash;
+		for (i = 0; i < delta; ++i) {
+			uint p = rng.random (h2.size() );
+			h2[p] = !h2[p];
+		}
+
+		h2.to_poly_cotrace (synd, fld);
+
+		std::cout << "SYND" << synd;
+
+		decsynd.clear();
+		for (i = 0; i < t; ++i)
+			decsynd.add_mult (syndS[i], synd[i], fld);
+
+		std::cout << "SYND PREP" << decsynd;
+
+		compute_goppa_error_locator (decsynd, fld, g, sqInv, loc);
+		if (!evaluate_error_locator_trace (loc, ev, fld) ) continue;
+		//we might have it!
+		std::cout << ev;
+		signature.clear();
+		signature.resize (signature_size(), 0);
+
+		for (i = 0; i < fld.n; ++i) if (ev[i]) {
+				uint epos = support_pos[i];
+				if (epos == fld.n) break; //bad luck, undecodable
+				signature[epos] = 1;
+			}
+		if (i == fld.n) return 0;
+	}
+	return 1; //no attempts left.
+}
+
+int pubkey::verify (const bvector&signature, const bvector&hash, uint delta)
+{
+	if (signature.size() != signature_size() ) return 2;
+	if (hash.size() != hash_size() ) return 2;
+
+	uint i, j;
+	uint block_size = 1 << T;
+	bvector synd, b1, b2;
+
+	synd.resize (t * qd_sigs.size(), 0);
+	//compute the syndrome
+	for (i = 0; i < signature_size(); ++i) {
+		if (!signature[i]) continue;
+
+		//this is actually quite fast, as it happens only several times
+		for (j = 0; j < qd_sigs.size(); ++j) {
+			qd_sigs[j].get_block ( (i / block_size) *block_size,
+			                       block_size, b1);
+			permutation::permute_dyadic (i % block_size, b1, b2);
+			b2.resize (t);
+			synd.add_offset (b2, t * j);
+		}
+	}
+
+	std::cout << "SYNDROME: " << synd;
+	synd.add (hash);
+	std::cout << "DIFF: " << synd;
+	if (synd.hamming_weight() > delta) return 1;
+
+	return 0;
+}
diff --git a/lib/decoding.cpp b/lib/decoding.cpp
index cd04824..c975a64 100644
--- a/lib/decoding.cpp
+++ b/lib/decoding.cpp
@@ -38,7 +38,7 @@ void compute_goppa_error_locator (polynomial&syndrome, gf2m&fld,
 	v.sqrt (sqInv, fld); //v = sqrt((1/s)+x) mod goppa
 
 	polynomial a, b;
-	v.ext_euclid (a, b, goppa, fld, goppa.degree()/2);
+	v.ext_euclid (a, b, goppa, fld, goppa.degree() / 2);
 
 	a.square (fld);
 	b.square (fld);
diff --git a/lib/mce_qd.cpp b/lib/mce_qd.cpp
index 95787ec..b697ff2 100644
--- a/lib/mce_qd.cpp
+++ b/lib/mce_qd.cpp
@@ -26,28 +26,6 @@ using namespace ccr::mce_qd;
 
 #include <set>
 
-static uint sample_from_u (gf2m&fld, prng&rng, std::set<uint>&used)
-{
-	uint x;
-	for (;;) {
-		x = rng.random (fld.n);
-		if (used.count (x) ) continue;
-		used.insert (x);
-		return x;
-	}
-}
-
-static uint choose_random (uint limit, prng&rng, std::set<uint>used)
-{
-	if (used.size() >= limit - 1) return 0; //die
-	for (;;) {
-		uint a = 1 + rng.random (limit - 1);
-		if (used.count (a) ) continue;
-		used.insert (a);
-		return a;
-	}
-}
-
 int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
                       uint m, uint T, uint block_discard)
 {
@@ -61,6 +39,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 
 	std::vector<uint> support, Hsig;
 	polynomial g;
+	uint i, j;
 
 	//prepare for data
 	Hsig.resize (fld.n / 2);
@@ -81,13 +60,13 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 		//essence[m-1] is now used as precomputed 1/h_0
 
 		for (uint s = 0; s < m - 1; ++s) {
-			uint i = 1 << s; //i = 2^s
+			i = 1 << s; //i = 2^s
 
 			Hsig[i] = choose_random (fld.n, rng, used);
 			essence[s] = fld.add (essence[m - 1], fld.inv (Hsig[i]) );
 			used.insert (fld.inv (essence[s]) );
 
-			for (uint j = 1; j < i; ++j) {
+			for (j = 1; j < i; ++j) {
 				Hsig[i + j] = fld.inv
 				              (fld.add
 				               (fld.inv (Hsig[i]),
@@ -111,7 +90,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 		polynomial tmp;
 		tmp.resize (2, 1); //tmp(x)=x-1
 		bool consistent = true;
-		for (uint i = 0; i < t; ++i) {
+		for (i = 0; i < t; ++i) {
 			//tmp(x)=x-z=x-(1/h_i)
 			tmp[0] = fld.inv (Hsig[i]);
 			if (used.count (tmp[0]) ) {
@@ -125,7 +104,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 		if (!consistent) continue; //retry
 
 		//compute the support, retry if it has two equal elements.
-		for (uint i = 0; i < fld.n / 2; ++i) {
+		for (i = 0; i < fld.n / 2; ++i) {
 			support[i] = fld.add (
 			                 fld.inv (Hsig[i]),
 			                 essence[m - 1]);
@@ -154,9 +133,9 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 		//assemble blocks to bl
 		std::vector<polynomial> bl, blp;
 		bl.resize (h_block_count);
-		for (uint i = 0; i < h_block_count; ++i) {
+		for (i = 0; i < h_block_count; ++i) {
 			bl[i].resize (block_size);
-			for (uint j = 0; j < block_size; ++j)
+			for (j = 0; j < block_size; ++j)
 				bl[i][j] = Hsig[i * block_size + j];
 		}
 
@@ -170,7 +149,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 		//permute individual blocks
 		priv.block_perms.resize (block_count);
 		bl.resize (blp.size() );
-		for (uint i = 0; i < block_count; ++i) {
+		for (i = 0; i < block_count; ++i) {
 			priv.block_perms[i] = rng.random (block_size);
 			permutation::permute_dyadic (priv.block_perms[i],
 			                             blp[i], bl[i]);
@@ -186,7 +165,6 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 
 			std::vector<std::vector<bvector> > hblocks;
 			bvector col;
-			uint i, j;
 
 			//prepare blocks of h
 			hblocks.resize (block_count);
@@ -206,10 +184,10 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 			 * If it fails, retry. */
 			if (!qd_to_right_echelon_form (hblocks) ) continue;
 
-			pub.qd_sigs.resize (block_count - fld.m);
-			for (uint i = 0; i < block_count - fld.m; ++i) {
-				pub.qd_sigs[i].resize (block_size * fld.m);
-				for (uint j = 0; j < fld.m; ++j)
+			pub.qd_sigs.resize2 (block_count - fld.m,
+			                     block_size * fld.m, 0);
+			for (i = 0; i < block_count - fld.m; ++i) {
+				for (j = 0; j < fld.m; ++j)
 					pub.qd_sigs[i].set_block
 					(hblocks[i][j], block_size * j);
 			}
@@ -233,6 +211,9 @@ int privkey::prepare()
 	std::vector<uint> Hsig, support;
 	uint omega;
 
+	uint block_size = 1 << T,
+	     block_count = hperm.size();
+
 	//compute H signature from essence
 	Hsig.resize (fld.n / 2);
 	Hsig[0] = fld.inv (essence[fld.m - 1]);
@@ -271,6 +252,8 @@ int privkey::prepare()
 	//compute the support with omega=0
 	support.resize (fld.n / 2);
 	for (i = 0; i < fld.n / 2; ++i) {
+		//don't check discarded support
+		if (block_perm[i / block_size] >= block_count) continue;
 		support[i] = fld.add
 		             (fld.inv (Hsig[i]),
 		              essence[fld.m - 1]);
@@ -305,9 +288,7 @@ int privkey::prepare()
 
 	// prepare permuted support, from that prepare permuted check matrix
 	// (so that it can be applied directly)
-	uint block_size = 1 << T;
 	uint pos, blk_perm;
-	uint block_count = hperm.size();
 	std::vector<uint> sbl1, sbl2, permuted_support;
 
 	sbl1.resize (block_size);
@@ -365,11 +346,11 @@ int pubkey::encrypt (const bvector & in, bvector & out, prng & rng)
 	 */
 
 	//some checks
-	if (!qd_sigs.size() ) return 1;
-	if (qd_sigs[0].size() % t) return 1;
+	if (!qd_sigs.width() ) return 1;
+	if (qd_sigs.height() % t) return 1;
 
-	uint blocks = qd_sigs[0].size() / t;
-	cksum.resize (qd_sigs[0].size(), 0);
+	uint blocks = qd_sigs.height() / t;
+	cksum.resize (qd_sigs.height(), 0);
 
 	p.resize (t);
 	g.resize (t);
diff --git a/lib/qd_utils.cpp b/lib/qd_utils.cpp
index 8ccb6b3..e4a75df 100644
--- a/lib/qd_utils.cpp
+++ b/lib/qd_utils.cpp
@@ -194,3 +194,15 @@ bool qd_to_right_echelon_form (std::vector<std::vector<bvector> >&mat)
 
 	return true;
 }
+
+uint choose_random (uint limit, prng&rng, std::set<uint>&used)
+{
+	if (used.size() >= limit - 1) return 0; //die
+	for (;;) {
+		uint a = 1 + rng.random (limit - 1);
+		if (used.count (a) ) continue;
+		used.insert (a);
+		return a;
+	}
+}
+
diff --git a/lib/qd_utils.h b/lib/qd_utils.h
index 47c9319..cd79006 100644
--- a/lib/qd_utils.h
+++ b/lib/qd_utils.h
@@ -20,6 +20,7 @@
 #define _qdutils_h_
 
 #include "codecrypt.h"
+#include <set>
 
 using namespace ccr;
 
@@ -29,5 +30,8 @@ void fwht_dyadic_multiply (const bvector&, const bvector&, bvector&);
 //create a generator using fwht
 bool qd_to_right_echelon_form (std::vector<std::vector<bvector> >&matrix);
 
+//disjunct random set selector. Doesn't select 0 (thus 0 is returned on failure)
+uint choose_random (uint limit, prng&rng, std::set<uint>&used);
+
 #endif
 
diff --git a/lib/serialization.cpp b/lib/serialization.cpp
index 311a7e0..394e563 100644
--- a/lib/serialization.cpp
+++ b/lib/serialization.cpp
@@ -3,6 +3,29 @@
 
 using namespace ccr;
 
+static sencode* serialize_uint_vector (std::vector<uint>*v)
+{
+	sencode_list*l = new sencode_list;
+	l->items.resize (v->size() );
+	for (uint i = 0; i < v->size(); ++i)
+		l->items[i] = new sencode_int ( (*v) [i]);
+	return l;
+}
+
+static bool unserialize_uint_vector (std::vector<uint>*v, sencode*s)
+{
+	sencode_list*l = dynamic_cast<sencode_list*> (s);
+	if (!l) return false;
+	v->clear();
+	v->resize (l->items.size() );
+	for (uint i = 0; i < v->size(); ++i) {
+		sencode_int*x = dynamic_cast<sencode_int*> (l->items[i]);
+		if (!x) return false;
+		(*v) [i] = x->i;
+	}
+	return true;
+}
+
 sencode* bvector::serialize()
 {
 	uint ss = (size() + 7) / 8;
@@ -68,25 +91,16 @@ bool matrix::unserialize (sencode* s)
 
 sencode* permutation::serialize()
 {
-	sencode_list*l = new sencode_list;
-	l->items.resize (size() );
-	for (uint i = 0; i < size(); ++i)
-		l->items[i] = new sencode_int (item (i) );
-	return l;
+	return serialize_uint_vector (this);
 }
 
 bool permutation::unserialize (sencode* s)
 {
-	sencode_list*l = dynamic_cast<sencode_list*> (s);
-	if (!l) return false;
-	clear();
-	resize (l->items.size() );
-	for (uint i = 0; i < size(); ++i) {
-		sencode_int*x = dynamic_cast<sencode_int*> (l->items[i]);
-		if (!x) return false;
-		if (x->i >= size() ) return false; //small sanity check
-		item (i) = x->i;
-	}
+	if (!unserialize_uint_vector (this, s) ) return false;
+
+	//small sanity check
+	for (uint i = 0; i < size(); ++i) if (item (i) >= size() ) return false;
+
 	return true;
 }
 
@@ -104,25 +118,12 @@ bool gf2m::unserialize (sencode* s)
 
 sencode* polynomial::serialize()
 {
-	sencode_list*l = new sencode_list;
-	l->items.resize (size() );
-	for (uint i = 0; i < size(); ++i)
-		l->items[i] = new sencode_int (item (i) );
-	return l;
+	return serialize_uint_vector (this);
 }
 
 bool polynomial::unserialize (sencode* s)
 {
-	sencode_list*l = dynamic_cast<sencode_list*> (s);
-	if (!l) return false;
-	clear();
-	resize (l->items.size() );
-	for (uint i = 0; i < size(); ++i) {
-		sencode_int*x = dynamic_cast<sencode_int*> (l->items[i]);
-		if (!x) return false;
-		item (i) = x->i;
-	}
-	return true;
+	return unserialize_uint_vector (this, s);
 }
 
 sencode* mce::privkey::serialize()
@@ -227,22 +228,58 @@ bool nd::pubkey::unserialize (sencode* s)
 
 sencode* mce_qd::privkey::serialize()
 {
-
+	sencode_list*l = new sencode_list;
+	l->items.resize (6);
+	l->items[0] = fld.serialize();
+	l->items[1] = new sencode_int (T);
+	l->items[2] = serialize_uint_vector (&essence);
+	l->items[3] = block_perm.serialize();
+	l->items[4] = serialize_uint_vector (&block_perms);
+	l->items[5] = hperm.serialize();
+	return l;
 }
 
 bool mce_qd::privkey::unserialize (sencode* s)
 {
+	sencode_list*l = dynamic_cast<sencode_list*> (s);
+	if (!l) return false;
+	if (l->items.size() != 6) return false;
 
+	sencode_int*p = dynamic_cast<sencode_int*> (l->items[1]);
+	if (!p) return false;
+	T = p->i;
+
+	if (! (fld.unserialize (l->items[0]) &&
+	       unserialize_uint_vector (&essence, l->items[2]) &&
+	       block_perm.unserialize (l->items[3]) &&
+	       unserialize_uint_vector (&block_perms, l->items[4]) &&
+	       hperm.unserialize (l->items[5]) ) ) return false;
+
+	return true;
 }
 
 sencode* mce_qd::pubkey::serialize()
 {
-
+	sencode_list*l = new sencode_list;
+	l->items.resize (2);
+	l->items[0] = new sencode_int (T);
+	l->items[1] = qd_sigs.serialize();
+	return l;
 }
 
 bool mce_qd::pubkey::unserialize (sencode* s)
 {
+	sencode_list*l = dynamic_cast<sencode_list*> (s);
+	if (!l) return false;
+	if (l->items.size() != 2) return false;
 
+	sencode_int*p = dynamic_cast<sencode_int*> (l->items[0]);
+	if (!p) return false;
+	T = p->i;
+
+	if (!qd_sigs.unserialize (l->items[1]) ) return false;
+
+	return true;
 }
 
 sencode* cfs_qd::privkey::serialize()