diff --git a/include/codecrypt.h b/include/codecrypt.h
index a8462e9..ffcc6db 100644
--- a/include/codecrypt.h
+++ b/include/codecrypt.h
@@ -388,6 +388,8 @@ public:
 	std::vector<uint> Hsig; //signature of canonical H matrix
 	std::vector<uint> support; //computed goppa support
 	polynomial g; //computed goppa polynomial
+	std::vector<polynomial> sqInv;
+
 
 	int decrypt (const bvector&, bvector&);
 	int prepare();
@@ -404,6 +406,7 @@ class pubkey
 {
 public:
 	uint T;
+	uint k;
 	std::vector<bvector> qd_sigs;
 
 	int encrypt (const bvector&, bvector&, prng&);
diff --git a/lib/fwht.cpp b/lib/fwht.cpp
new file mode 100644
index 0000000..e913c52
--- /dev/null
+++ b/lib/fwht.cpp
@@ -0,0 +1,53 @@
+
+#include "fwht.h"
+
+#include <vector>
+using namespace std;
+
+/*
+ * we count on that all integers are sufficiently large.
+ * They should be, largest value occuring should be O(k*n) if initial vector is
+ * consisted only from {0,1}^n, and we don't usually have codes of this size.
+ */
+
+static void fwht (vector<int> x, vector<int>&r)
+{
+	int bs, s;
+	s = x.size();
+	r.resize (s);
+	bs = s >> 1;
+	r.swap (x);
+	while (bs) {
+		x.swap (r);
+		for (uint i = 0; i < s; ++i) {
+			if ( (i / bs) & 1)
+				r[i] = x[i-bs] - x[i];
+			else
+				r[i] = x[i] + x[i+bs];
+		}
+		bs >>= 1;
+	}
+}
+
+//we expect correct parameter size and preallocated out.
+void fwht_dyadic_multiply (const bvector& a, const bvector& b, bvector& out)
+{
+
+	//lift everyting to Z.
+	vector<int> t, A, B;
+	uint i;
+
+	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;
+}
+
diff --git a/lib/fwht.h b/lib/fwht.h
new file mode 100644
index 0000000..a7e51d1
--- /dev/null
+++ b/lib/fwht.h
@@ -0,0 +1,13 @@
+
+#ifndef _fwht_h_
+#define _fwht_h_
+
+#include "codecrypt.h"
+
+using namespace ccr;
+
+//parameters MUST be of 2^m size.
+void fwht_dyadic_multiply (const bvector&, const bvector&, bvector&);
+
+#endif
+
diff --git a/lib/mce_qd.cpp b/lib/mce_qd.cpp
index de1203f..aba93b7 100644
--- a/lib/mce_qd.cpp
+++ b/lib/mce_qd.cpp
@@ -70,18 +70,18 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 			used.insert (fld.inv (essence[s]) );
 
 			for (uint j = 1; j < i; ++j) {
-				Hsig[i+j] = fld.inv (
-				                fld.add (
-				                    fld.inv (Hsig[i]),
-				                    fld.add (
-				                        fld.inv (Hsig[j]),
-				                        essence[m-1]
-				                    ) ) );
+				Hsig[i+j] = fld.inv
+				            (fld.add
+				             (fld.inv (Hsig[i]),
+				              fld.add (
+				                  fld.inv (Hsig[j]),
+				                  essence[m-1]
+				              ) ) );
 				used.insert (Hsig[i+j]);
-				used.insert (fld.inv (
-				                 fld.add (
-				                     fld.inv (Hsig[i+j]),
-				                     essence[m-1]) ) );
+				used.insert (fld.inv
+				             (fld.add
+				              (fld.inv (Hsig[i+j]),
+				               essence[m-1]) ) );
 			}
 		}
 
@@ -182,6 +182,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 		 */
 
 		pub.T = T;
+		pub.k = (block_count - fld.m) * block_size;
 		pub.qd_sigs.resize (ri.width() / t);
 		for (uint i = 0; i < ri.width(); i += t)
 			pub.qd_sigs[i/t] = ri[i];
@@ -192,14 +193,105 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
 
 int privkey::prepare()
 {
-	//TODO compute H signature from essence
-	//TODO compute goppa code support
+	//compute H signature from essence
+	Hsig.resize (fld.n / 2);
+	Hsig[0] = fld.inv (essence[fld.m-1]);
+	for (uint s = 0; s < fld.m - 1; ++s) {
+		uint i = 1 << s; //i = 2^s
+
+		//TODO verify this
+		Hsig[i] = fld.inv (fld.add (essence[s], essence[fld.m-1]) );
+
+		for (uint 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-1]
+			              ) ) );
+	}
+
+	//compute the support
+	support.resize (fld.n / 2);
+	for (uint i = 0; i < fld.n / 2; ++i) {
+		support[i] = fld.add
+		             (fld.inv (Hsig[i]),
+		              essence[fld.m-1]);
+
+	}
+
+	//goppa polynomial
+	g.clear();
+	g.resize (1, 1);
+	polynomial tmp;
+	tmp.resize (2, 1);
+	uint t = 1 << T;
+	for (uint i = 0; i < t; ++i) {
+		tmp[0] = fld.inv (Hsig[i]);
+		g.mult (tmp, fld);
+	}
+
+	//sqInv
+	g.compute_square_root_matrix (sqInv, fld);
+
 	return 0;
 }
 
+#include "fwht.h"
+
 int pubkey::encrypt (const bvector& in, bvector&out, prng&rng)
 {
-	//TODO FWHT
+	uint t = 1 << T;
+	bvector p, g, r, cksum;
+
+	/*
+	 * shortened checksum pair of G is computed blockwise accordingly to
+	 * the t-sized square dyadic blocks.
+	 */
+
+	//some checks
+	if (in.size() != k) return 1;
+	if (!qd_sigs.size() ) return 1;
+	if (qd_sigs[0].size() % t) return 1;
+
+	uint blocks = qd_sigs[0].size() / t;
+	cksum.resize (qd_sigs[0].size(), 0);
+
+	p.resize (t);
+	g.resize (t);
+	r.resize (t);
+
+	for (uint i = 0; i < blocks; ++i) {
+		//plaintext block
+		for (uint k = 0; k < t; ++k) p[k] = in[k+i*t];
+
+		for (uint j = 0; j < qd_sigs.size(); ++j) {
+			//checksum block
+			for (uint k = 0; k < t; ++k) g[k] = qd_sigs[i][k+j*t];
+
+			//block result
+			fwht_dyadic_multiply (p, g, r);
+			cksum.add_offset (r, t * j);
+		}
+	}
+
+	//generate t errors
+	bvector e;
+	e.resize (k + qd_sigs[0].size(), 0);
+	for (uint n = t; n > 0;) {
+		uint p = rng.random (e.size() );
+		if (!e[p]) {
+			e[p] = 1;
+			--n;
+		}
+	}
+
+	//compute ciphertext
+	out = in;
+	out.insert (out.end(), cksum.begin(), cksum.end() );
+	out.add (e);
+
 	return 0;
 }