From 21b3ef85d18686b57969d6209e9d808d285a5a60 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Wed, 18 Jul 2012 11:17:36 +0200 Subject: [PATCH] mce_oc signature implementation + some fixes + mce_qd skeleton --- include/codecrypt.h | 143 ++++++++++++++++++++++++++++++++++++++-- lib/matrix.cpp | 10 +++ lib/mce.cpp | 2 +- lib/mce_oc.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++ lib/mce_qd.cpp | 34 ++++++++++ src/main.cpp | 25 ++++--- 6 files changed, 347 insertions(+), 22 deletions(-) create mode 100644 lib/mce_oc.cpp create mode 100644 lib/mce_qd.cpp diff --git a/include/codecrypt.h b/include/codecrypt.h index 0bb4ee2..03dea47 100644 --- a/include/codecrypt.h +++ b/include/codecrypt.h @@ -10,6 +10,13 @@ { return (*this)[n]; }; \ inline const_reference item(size_type n) const \ { return (*this)[n]; }; +#define _ccr_declare_matrix_item \ + inline value_type::reference \ + item(size_type n, size_type m) \ + { return (*this)[n][m]; }; \ + inline value_type::const_reference \ + item(size_type n, size_type m) const \ + { return (*this)[n][m]; }; namespace ccr { @@ -62,6 +69,7 @@ class matrix : public std::vector { protected: _ccr_declare_vector_item + _ccr_declare_matrix_item public: uint width() const { return size(); @@ -75,20 +83,24 @@ public: matrix operator* (const matrix&); void mult (const matrix&); //right multiply - this*param - void compute_transpose (matrix&); - bool compute_inversion (matrix&); - void generate_random_invertible (uint, prng&); void unit (uint); + + void compute_transpose (matrix&); + bool mult_vecT_left (const bvector&, bvector&); + bool mult_vec_right (const bvector&, bvector&); + bool compute_inversion (matrix&); + + bool set_block (uint, uint, const matrix&); bool get_left_square (matrix&); bool strip_left_square (matrix&); bool get_right_square (matrix&); bool strip_right_square (matrix&); void extend_left_compact (matrix&); + + void generate_random_invertible (uint, prng&); bool create_goppa_generator (matrix&, permutation&, prng&); bool create_goppa_generator (matrix&, const permutation&); - bool mult_vecT_left (const bvector&, bvector&); - bool mult_vec_right (const bvector&, bvector&); }; /* @@ -295,6 +307,125 @@ public: int generate (pubkey&, privkey&, prng&, uint m, uint t); } +/* + * compact Quasi-dyadic McEliece + * according to Misoczki, Barreto, Compact McEliece Keys from Goppa Codes. + * + * Good security, extremely good speed with extremely reduced key size. + * Recommended for encryption. + */ +namespace mce_qd +{ +class privkey +{ +public: + int decrypt (const bvector&, bvector&); + int prepare(); + + uint cipher_size() { + return 0; //TODO + } + uint plain_size() { + return 0; //TODO + } + uint hash_size() { + return 0; //TODO + } + uint signature_size() { + return 0; //TODO + } +}; + +class pubkey +{ +public: + matrix G; + uint t; + + int encrypt (const bvector&, bvector&, prng&); + + uint cipher_size() { + return G.height(); + } + uint plain_size() { + return G.width(); + } + uint hash_size() { + return cipher_size(); + } + uint signature_size() { + return plain_size(); + } +}; + +int generate (pubkey&, privkey&, prng&, uint m, uint t); +} + +/* + * McEliece on Overlapping Chain of Goppa Codes + * + * Similar to Hamdi's Chained BCH Codes, but with improvement. + * + * This is experimental, unverified, probably insecure, but practical scheme + * that achieves good speed, probability and key size for full decoding that is + * needed to produce signatures. Technique is described in documentation, with + * some (probably sufficient) notes in source code. + * + * Note that encryption using this scheme is absolutely impractical. + */ +namespace mce_oc +{ +class privkey +{ +public: + matrix Sinv; + permutation Pinv; + gf2m fld; + + class subcode + { + public: + polynomial g; + permutation hperm; + + //derivables + matrix h; + std::vector sqInv; + }; + + std::vector codes; + + int sign (const bvector&, bvector&, uint, uint, prng&); + int prepare(); + + uint hash_size() { + return Pinv.size(); + } + uint signature_size() { + return Sinv.size(); + } +}; + +class pubkey +{ +public: + matrix G; + uint n, t; + + int verify (const bvector&, const bvector&, uint); + + uint hash_size() { + return G.width(); + } + uint signature_size() { + return G.height(); + } +}; + +//n is the number of subcodes used +int generate (pubkey&, privkey&, prng&, uint m, uint t, uint n); +} + } //namespace ccr //global overload for iostream operators @@ -307,7 +438,5 @@ std::ostream& operator<< (std::ostream&o, const ccr::matrix&); std::ostream& operator<< (std::ostream&o, const ccr::bvector&); - - #endif // _CODECRYPT_H_ diff --git a/lib/matrix.cpp b/lib/matrix.cpp index ae3918e..22ef3cf 100644 --- a/lib/matrix.cpp +++ b/lib/matrix.cpp @@ -211,3 +211,13 @@ bool matrix::mult_vec_right (const bvector&a, bvector&r) if (a[i]) r.add (item (i) ); return true; } + +bool matrix::set_block (uint x, uint y, const matrix&b) +{ + uint h = b.height(), w = b.width(); + if (width() < x + w) return false; + if (height() < y + h) return false; + for (uint i = 0; i < w; ++i) + for (uint j = 0; j < h; ++j) item (x + i, y + j) = b.item (i, j); + return true; +} diff --git a/lib/mce.cpp b/lib/mce.cpp index af49c03..5c5ce25 100644 --- a/lib/mce.cpp +++ b/lib/mce.cpp @@ -145,8 +145,8 @@ int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prn 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.add (h[epos[i]]); } compute_error_locator (synd, fld, g, sqInv, loc); diff --git a/lib/mce_oc.cpp b/lib/mce_oc.cpp new file mode 100644 index 0000000..86735de --- /dev/null +++ b/lib/mce_oc.cpp @@ -0,0 +1,155 @@ + +#include "codecrypt.h" + +using namespace ccr; +using namespace ccr::mce_oc; + +#include "decoding.h" + +int mce_oc::generate (pubkey&pub, privkey&priv, prng&rng, uint m, uint t, uint n) +{ + priv.fld.create (m); + + uint subplain_size = priv.fld.n - (m * t), + codeword_size = (n * subplain_size) + (m * t); + + //prepare resulting generator matrix + matrix g; + g.resize (codeword_size); + for (uint i = 0; i < codeword_size; ++i) + g[i].resize (subplain_size * n); + + //generate n subcodes + priv.codes.resize (n); + for (uint i = 0; i < n; ++i) { + privkey::subcode& sc = priv.codes[i]; + + sc.g.generate_random_irreducible (t, priv.fld, rng); + sc.g.compute_goppa_check_matrix (sc.h, priv.fld); + + matrix subg; + while (!sc.h.create_goppa_generator (subg, sc.hperm, rng) ); + g.set_block (subplain_size * i, subplain_size * i, subg); + } + + //scramble matrix + matrix S; + S.generate_random_invertible (g.height(), rng); + S.compute_inversion (priv.Sinv); + + //scramble permutation + permutation P; + P.generate_random (g.width(), rng); + P.compute_inversion (priv.Pinv); + + //public key + pub.n = n; + pub.t = t; + S.mult (g); + P.permute (S, pub.G); + + return 0; +} + +int privkey::prepare () +{ + for (uint i = 0; i < codes.size(); ++i) { + codes[i].g.compute_goppa_check_matrix (codes[i].h, fld); + codes[i].g.compute_square_root_matrix (codes[i].sqInv, fld); + } + return 0; +} + +int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prng&rng) +{ + if (in.size() != hash_size() ) return 2; + if (!codes.size() ) return 2; + + //remove permutation + bvector inp; + Pinv.permute (in, inp); + + //decoding helpers + bvector e, e2, synd, synd_orig, cw, cwc, plain; + std::vector epos; + permutation hpermInv; + polynomial loc; + uint i, t; + + plain.clear(); + + //decode the rest + for (uint ci = 0; ci < codes.size(); ++ci) { + + uint mt = fld.m * codes[ci].g.degree(), + subplain_size = fld.n - mt; + + e.clear(); + e.resize (fld.n, 0); + epos.resize (delta, 0); + + //decode first subcode + cw.clear(); + if (ci == 0) + cw.insert (cw.end(), inp.begin(), inp.begin() + fld.n); + else { + cw.resize (mt, 0); + bvector::iterator tmp = inp.begin(); + tmp += (ci * subplain_size) + mt; + cw.insert (cw.end(), tmp, tmp + subplain_size); + } + + //compute syndrome with no extra errors + codes[ci].hperm.compute_inversion (hpermInv); + hpermInv.permute (cw, cwc); //canonical + codes[ci].h.mult_vec_right (cwc, synd_orig); + + for (t = 0; t < attempts; ++t) { + + //compute syndrome with extra errors + synd = synd_orig; + for (i = 0; i < delta; ++i) { + epos[i] = rng.random (fld.n); + if (!e[epos[i]]) synd.add (codes[ci].h[epos[i]]); + e[epos[i]] = 1; + } + + compute_error_locator (synd, fld, + codes[ci].g, + codes[ci].sqInv, loc); + + if (evaluate_error_locator_trace (loc, e2, fld) ) { + cwc.add (e); + cwc.add (e2); + + codes[ci].hperm.permute (cwc, cw); + plain.insert (plain.end(), cw.begin(), + cw.begin() + (fld.n - (fld.m * codes[ci].g.degree() ) ) ); + break; + } + + for (i = 0; i < delta; ++i) { + e[epos[i]] = 0; + } + } + + if (t >= attempts) //decoding failed + return 1; + + } + + Sinv.mult_vecT_left (plain, out); + + return 0; +} + +int pubkey::verify (const bvector&in, const bvector&hash, uint delta) +{ + bvector tmp; + if (!G.mult_vecT_left (in, tmp) ) return 2; //sizing problem + if (hash.size() != tmp.size() ) return 1; //invalid hash size + + tmp.add (hash); + if (tmp.hamming_weight() > n* (t + delta) ) return 1; //too far + return 0; +} diff --git a/lib/mce_qd.cpp b/lib/mce_qd.cpp new file mode 100644 index 0000000..1774126 --- /dev/null +++ b/lib/mce_qd.cpp @@ -0,0 +1,34 @@ + +#include "codecrypt.h" + +using namespace ccr; +using namespace ccr::mce_qd; + +#include "decoding.h" + +int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, uint m, uint t) +{ + return 0; +} + +int pubkey::encrypt (const bvector& in, bvector&out, prng&rng) +{ + uint s = cipher_size(); + if (t > s) return 1; + if (in.size() != plain_size() ) return 2; + + return 0; +} + +int privkey::decrypt (const bvector&in, bvector&out) +{ + if (in.size() != cipher_size() ) return 2; + + return 0; +} + +int privkey::prepare () +{ + return 0; +} + diff --git a/src/main.cpp b/src/main.cpp index 078d675..0872f76 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,22 +24,18 @@ int main() primitiverng r; r.seed (0); - ccr::mce::privkey priv; - ccr::mce::pubkey pub; - ccr::mce::generate (pub, priv, r, 7, 2); + ccr::mce_oc::privkey priv; + ccr::mce_oc::pubkey pub; + ccr::mce_oc::generate (pub, priv, r, 7, 2, 8); + + priv.prepare(); - cout << "PRIVATE KEY" << endl; - cout << priv.fld; - cout << priv.hperm; - cout << priv.Pinv; - cout << priv.Sinv; - cout << priv.g; cout << "PUBLIC KEY" << endl; cout << pub.t << endl; cout << pub.G; +#if 0 /* mce encryption test */ - ccr::bvector plain; plain.resize (pub.plain_size(), 0); plain[0] = 1; @@ -50,18 +46,19 @@ int main() cout << plain; ccr::bvector cipher; - pub.encrypt (plain, cipher, r); + //pub.encrypt (plain, cipher, r); + pub.encrypt (plain, cipher, r, 10); cout << "CIPHERTEXT" << endl; cout << cipher; - priv.prepare(); ccr::bvector result; priv.decrypt (cipher, result); cout << "DECRYPTED" << endl; cout << result; +#endif /* signature test */ @@ -73,9 +70,9 @@ int main() hash[2] = 1; cout << "SIGNING" << endl << hash; - priv.sign (hash, signature, 3, priv.hash_size() *priv.hash_size(), r); + priv.sign (hash, signature, 2, priv.hash_size() *priv.hash_size(), r); cout << "SIGNATURE" << endl << signature; - if (pub.verify (signature, hash, 3) ) + if (pub.verify (signature, hash, 2) ) cout << "VERIFY FAIL" << endl; else cout << "VERIFY OK" << endl; return 0;