1
0
mirror of https://github.com/biergaizi/codecrypt synced 2024-06-16 03:48:19 +00:00

mce_qd: remove obsolete MCE-QD encryption

This commit is contained in:
Mirek Kratochvil 2016-04-28 13:06:55 +02:00
parent 77c626aab2
commit 8e608b4d8d
8 changed files with 28 additions and 1144 deletions

@ -28,16 +28,6 @@ void fill_algorithm_suite (algorithm_suite&s)
#define do_alg(x) static x var_##x ; var_##x.register_into_suite(s);
#if HAVE_CRYPTOPP==1
do_alg (algo_mceqd128);
do_alg (algo_mceqd192);
do_alg (algo_mceqd256);
do_alg (algo_mceqd128cha);
do_alg (algo_mceqd192cha);
do_alg (algo_mceqd256cha);
do_alg (algo_mceqd128xs);
do_alg (algo_mceqd192xs);
do_alg (algo_mceqd256xs);
do_alg (algo_mceqcmdpc128);
do_alg (algo_mceqcmdpc256);
do_alg (algo_mceqcmdpc128cha);
@ -52,17 +42,6 @@ void fill_algorithm_suite (algorithm_suite&s)
do_alg (algo_fmtseq192h20);
do_alg (algo_fmtseq256h20);
#endif //HAVE_CRYPTOPP==1
do_alg (algo_mceqd128cube);
do_alg (algo_mceqd192cube);
do_alg (algo_mceqd256cube);
do_alg (algo_mceqd128cubecha);
do_alg (algo_mceqd192cubecha);
do_alg (algo_mceqd256cubecha);
do_alg (algo_mceqd128cubexs);
do_alg (algo_mceqd192cubexs);
do_alg (algo_mceqd256cubexs);
do_alg (algo_mceqcmdpc128cube);
do_alg (algo_mceqcmdpc256cube);
do_alg (algo_mceqcmdpc128cubecha);

@ -20,7 +20,6 @@
#include "algos_enc.h"
#include "mce_qd.h"
#include "mce_qcmdpc.h"
#include "arcfour.h"
#include "chacha.h"
@ -38,20 +37,6 @@ typedef arcfour<byte, 8, 4096> arcfour_fo_cipher;
* keygen
*/
template<int m, int T, int b, int d>
static int mceqd_create_keypair (sencode**pub, sencode**priv, prng&rng)
{
mce_qd::pubkey Pub;
mce_qd::privkey Priv;
if (mce_qd::generate (Pub, Priv, rng, m, T, b, d))
return 1;
*pub = Pub.serialize();
*priv = Priv.serialize();
return 0;
}
template<int bs, int bc, int wi, int t, int rounds, int delta>
static int mceqcmdpc_create_keypair (sencode**pub, sencode**priv, prng&rng)
{
@ -488,88 +473,3 @@ mceqcmdpc_create_encdec_func (128cubecha, 9857, 2, 134, cube256hash, cube128hash
mceqcmdpc_create_encdec_func (256cubecha, 32771, 2, 264, cube512hash, cube256hash, chacha20, 2475)
mceqcmdpc_create_encdec_func (128cubexs, 9857, 2, 134, cube256hash, cube128hash, xsynd, 1152)
mceqcmdpc_create_encdec_func (256cubexs, 32771, 2, 264, cube512hash, cube256hash, xsynd, 2475)
/*
* Instances for MCE-QD algorithms
*/
#define mceqd_create_keypair_func(name,m,T,b,d) \
int algo_mceqd##name::create_keypair (sencode**pub, sencode**priv, prng&rng) \
{ \
return mceqd_create_keypair<m, T, b, d> (pub, priv, rng); \
}
#if HAVE_CRYPTOPP==1
mceqd_create_keypair_func (128, 16, 7, 32, 4)
mceqd_create_keypair_func (192, 16, 8, 27, 4)
mceqd_create_keypair_func (256, 16, 8, 32, 4)
mceqd_create_keypair_func (128cha, 16, 7, 32, 4)
mceqd_create_keypair_func (192cha, 16, 8, 27, 4)
mceqd_create_keypair_func (256cha, 16, 8, 32, 4)
mceqd_create_keypair_func (128xs, 16, 7, 32, 4)
mceqd_create_keypair_func (192xs, 16, 8, 27, 4)
mceqd_create_keypair_func (256xs, 16, 8, 32, 4)
#endif //HAVE_CRYPTOPP==1
mceqd_create_keypair_func (128cube, 16, 7, 32, 4)
mceqd_create_keypair_func (192cube, 16, 8, 27, 4)
mceqd_create_keypair_func (256cube, 16, 8, 32, 4)
mceqd_create_keypair_func (128cubecha, 16, 7, 32, 4)
mceqd_create_keypair_func (192cubecha, 16, 8, 27, 4)
mceqd_create_keypair_func (256cubecha, 16, 8, 32, 4)
mceqd_create_keypair_func (128cubexs, 16, 7, 32, 4)
mceqd_create_keypair_func (192cubexs, 16, 8, 27, 4)
mceqd_create_keypair_func (256cubexs, 16, 8, 32, 4)
#define mceqd_create_encdec_func(name,plainsize,ciphersize,errcount, hash_type,pad_hash_type,scipher,ranksize) \
int algo_mceqd##name::encrypt (const bvector&plain, bvector&cipher, \
sencode* pubkey, prng&rng) \
{ \
return fo_encrypt \
< mce_qd::pubkey, \
plainsize, ciphersize, errcount, \
hash_type, \
pad_hash_type, \
scipher, \
ranksize > \
(plain, cipher, pubkey, rng); \
} \
int algo_mceqd##name::decrypt (const bvector&cipher, bvector&plain, \
sencode* privkey) \
{ \
return fo_decrypt \
< mce_qd::privkey, \
plainsize, ciphersize, errcount, \
hash_type, \
pad_hash_type, \
scipher, \
ranksize > \
(cipher, plain, privkey); \
}
#if HAVE_CRYPTOPP==1
mceqd_create_encdec_func (128, 2048, 4096, 128, sha256hash, rmd128hash, arcfour_fo_cipher, 816)
mceqd_create_encdec_func (192, 2816, 6912, 256, sha384hash, rmd128hash, arcfour_fo_cipher, 1574)
mceqd_create_encdec_func (256, 4096, 8192, 256, sha512hash, rmd128hash, arcfour_fo_cipher, 1638)
mceqd_create_encdec_func (128cha, 2048, 4096, 128, sha256hash, rmd128hash, chacha20, 816)
mceqd_create_encdec_func (192cha, 2816, 6912, 256, sha384hash, rmd128hash, chacha20, 1574)
mceqd_create_encdec_func (256cha, 4096, 8192, 256, sha512hash, rmd128hash, chacha20, 1638)
mceqd_create_encdec_func (128xs, 2048, 4096, 128, sha256hash, rmd128hash, xsynd, 816)
mceqd_create_encdec_func (192xs, 2816, 6912, 256, sha384hash, rmd128hash, xsynd, 1574)
mceqd_create_encdec_func (256xs, 4096, 8192, 256, sha512hash, rmd128hash, xsynd, 1638)
#endif //HAVE_CRYPTOPP==1
mceqd_create_encdec_func (128cube, 2048, 4096, 128, cube256hash, cube128hash, arcfour_fo_cipher, 816)
mceqd_create_encdec_func (192cube, 2816, 6912, 256, cube384hash, cube128hash, arcfour_fo_cipher, 1574)
mceqd_create_encdec_func (256cube, 4096, 8192, 256, cube512hash, cube128hash, arcfour_fo_cipher, 1638)
mceqd_create_encdec_func (128cubecha, 2048, 4096, 128, cube256hash, cube128hash, chacha20, 816)
mceqd_create_encdec_func (192cubecha, 2816, 6912, 256, cube384hash, cube128hash, chacha20, 1574)
mceqd_create_encdec_func (256cubecha, 4096, 8192, 256, cube512hash, cube128hash, chacha20, 1638)
mceqd_create_encdec_func (128cubexs, 2048, 4096, 128, cube256hash, cube128hash, xsynd, 816)
mceqd_create_encdec_func (192cubexs, 2816, 6912, 256, cube384hash, cube128hash, xsynd, 1574)
mceqd_create_encdec_func (256cubexs, 4096, 8192, 256, cube512hash, cube128hash, xsynd, 1638)

@ -49,16 +49,6 @@ public: \
* SHA-based variants
*/
mce_alg_class (qd128, "MCEQD128FO-SHA256-ARCFOUR");
mce_alg_class (qd192, "MCEQD192FO-SHA384-ARCFOUR");
mce_alg_class (qd256, "MCEQD256FO-SHA512-ARCFOUR");
mce_alg_class (qd128cha, "MCEQD128FO-SHA256-CHACHA20");
mce_alg_class (qd192cha, "MCEQD192FO-SHA384-CHACHA20");
mce_alg_class (qd256cha, "MCEQD256FO-SHA512-CHACHA20");
mce_alg_class (qd128xs, "MCEQD128FO-SHA256-XSYND");
mce_alg_class (qd192xs, "MCEQD192FO-SHA384-XSYND");
mce_alg_class (qd256xs, "MCEQD256FO-SHA512-XSYND");
mce_alg_class (qcmdpc128, "MCEQCMDPC128FO-SHA256-ARCFOUR");
mce_alg_class (qcmdpc256, "MCEQCMDPC256FO-SHA512-ARCFOUR");
mce_alg_class (qcmdpc128cha, "MCEQCMDPC128FO-SHA256-CHACHA20");
@ -72,16 +62,6 @@ mce_alg_class (qcmdpc256xs, "MCEQCMDPC256FO-SHA512-XSYND");
* Cubehash-based variants
*/
mce_alg_class (qd128cube, "MCEQD128FO-CUBE256-ARCFOUR");
mce_alg_class (qd192cube, "MCEQD192FO-CUBE384-ARCFOUR");
mce_alg_class (qd256cube, "MCEQD256FO-CUBE512-ARCFOUR");
mce_alg_class (qd128cubecha, "MCEQD128FO-CUBE256-CHACHA20");
mce_alg_class (qd192cubecha, "MCEQD192FO-CUBE384-CHACHA20");
mce_alg_class (qd256cubecha, "MCEQD256FO-CUBE512-CHACHA20");
mce_alg_class (qd128cubexs, "MCEQD128FO-CUBE256-XSYND");
mce_alg_class (qd192cubexs, "MCEQD192FO-CUBE384-XSYND");
mce_alg_class (qd256cubexs, "MCEQD256FO-CUBE512-XSYND");
mce_alg_class (qcmdpc128cube, "MCEQCMDPC128FO-CUBE256-ARCFOUR");
mce_alg_class (qcmdpc256cube, "MCEQCMDPC256FO-CUBE512-ARCFOUR");
mce_alg_class (qcmdpc128cubecha, "MCEQCMDPC128FO-CUBE256-CHACHA20");

@ -1,123 +0,0 @@
/*
* This file is part of Codecrypt.
*
* Copyright (C) 2013-2016 Mirek Kratochvil <exa.exa@gmail.com>
*
* 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 "decoding.h"
void compute_alternant_error_locator (polynomial&syndrome, gf2m&fld,
uint t, polynomial&out)
{
if (syndrome.zero()) {
//ensure no roots
out.resize (1);
out[0] = 1;
return;
}
polynomial a, b;
polynomial x2t; //should be x^2t
x2t.clear();
x2t.resize (1, 1);
x2t.shift (2 * t);
syndrome.ext_euclid (a, b, x2t, fld, t - 1);
uint b0inv = fld.inv (b[0]);
for (uint i = 0; i < b.size(); ++i) b[i] = fld.mult (b[i], b0inv);
out = b;
//we don't care about error evaluator
}
/*
* berlekamp trace algorithm - we puncture roots of incoming polynomial into
* the vector of size fld.n
*
* Inspired by implementation from HyMES.
*/
#include <set>
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;
}

@ -1,470 +0,0 @@
/*
* This file is part of Codecrypt.
*
* Copyright (C) 2013-2016 Mirek Kratochvil <exa.exa@gmail.com>
*
* 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 "mce_qd.h"
using namespace mce_qd;
#include "decoding.h"
#include "qd_utils.h"
#include "iohelpers.h"
#include <set>
static void print_attack_warning()
{
static bool printed = false;
if (printed) return;
err ("\n***MCEQD SECURITY WARNING***\n\n"
"Security of the QD-McEliece variant was greatly reduced to less than 2^30\n"
"by an algebraic attack! The functions are kept only for compatibility\n"
"and will be removed soon. Use `-g help' for other encryption variants.");
printed = true;
}
int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng,
uint m, uint T, uint block_count, uint block_discard)
{
print_attack_warning();
//convenience
gf2m&fld = priv.fld;
std::vector<uint>&essence = priv.essence;
//initial stuff and sizes
fld.create (m);
priv.T = T;
uint t = 1 << T,
block_size = t,
h_block_count = block_count + block_discard,
n = h_block_count * t;
if (block_count <= m) return 2; //lower bound on block_count
if (n > fld.n / 2) return 2; //n <= q/2
std::vector<uint> support, Hsig;
polynomial g;
uint i, j;
//prepare data arrays
Hsig.resize (n);
support.resize (n);
essence.resize (m);
//retry generating until goppa code is produced.
for (;;) {
std::set<uint> used;
used.clear();
//first off, compute the H signature
Hsig[0] = choose_random (fld.n, rng, used);
essence[m - 1] = fld.inv (Hsig[0]);
//essence[m-1] is now used as precomputed 1/h_0
for (uint s = 0; ( (uint) 1 << s) < n; ++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 (j = 1; j < i; ++j) {
if (i + j >= n) break;
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])));
}
}
//assemble goppa polynomial.
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)
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
//compute the support, retry if it has two equal elements.
for (i = 0; i < n; ++i) {
support[i] = fld.add (
fld.inv (Hsig[i]),
essence[m - 1]);
if (used.count (support[i])) {
consistent = false;
break;
}
used.insert (support[i]);
}
if (!consistent) continue; //retry
//now the blocks. First 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 them
priv.block_perm.generate_random (h_block_count, rng);
priv.block_perm.permute (bl, blp);
//discard blocks
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]);
}
//try several permutations to construct G
uint attempts = 0;
for (attempts = 0; attempts < block_count; ++attempts) {
priv.hperm.generate_random (block_count, rng);
permutation hpermInv;
priv.hperm.compute_inversion (hpermInv);
std::vector<std::vector<bvector> > hblocks;
bvector col;
//prepare blocks of h
hblocks.resize (block_count);
for (i = 0; i < block_count; ++i)
hblocks[i].resize (fld.m);
//fill them from Hsig
for (i = 0; i < block_count; ++i) {
col.from_poly_cotrace (bl[hpermInv[i]], fld);
for (j = 0; j < fld.m; ++j)
col.get_block (j * block_size,
block_size,
hblocks[i][j]);
}
/* do a modified QD-blockwise gaussian elimination on hblocks.
* If it fails, retry. */
if (!qd_to_right_echelon_form (hblocks)) continue;
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);
}
break;
}
if (attempts == block_count) //generating G failed, retry all
continue;
//finish the pubkey
pub.T = T;
return 0;
}
}
int privkey::prepare()
{
print_attack_warning();
uint s, i, j;
std::vector<uint> Hsig, support;
uint omega;
uint block_size = 1 << T,
block_count = hperm.size(),
h_block_count = block_perm.size(),
n = h_block_count * block_size;
//compute H signature from essence
Hsig.resize (n);
Hsig[0] = fld.inv (essence[fld.m - 1]);
for (s = 0; ( (uint) 1 << s) < n; ++s) {
i = 1 << s; //i = 2^s
Hsig[i] = fld.inv (fld.add (essence[s], essence[fld.m - 1]));
for (j = 1; j < i; ++j) {
if (i + j >= n) break;
Hsig[i + j] = fld.inv
(fld.add
(fld.inv (Hsig[i]),
fld.add (
fld.inv (Hsig[j]),
essence[fld.m - 1]
)));
}
}
//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 < block_size; ++i) {
tmp[0] = fld.inv (Hsig[i]); //tmp(x)=x+1/h_i
if (used.count (tmp[0]))
return 1;
used.insert (tmp[0]);
g.mult (tmp, fld);
}
//compute the support with omega=0
support.resize (n);
for (i = 0; i < n; ++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]);
//support consistency check
if (used.count (support[i]))
return 1;
used.insert (support[i]);
}
//choose some 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 < block_size; ++i) {
tmp[0] = fld.add (fld.inv (Hsig[i]), omega);
g.mult (tmp, fld);
}
// prepare permuted support, from that prepare permuted check matrix
// (so that it can be applied directly)
uint pos, blk_perm;
std::vector<uint> sbl1, sbl2;
sbl1.resize (block_size);
sbl2.resize (block_size);
permuted_support.resize (block_size * block_count);
//permute support
for (i = 0; i < h_block_count; ++i) {
pos = block_perm[i];
if (pos >= block_count) continue; //was discarded
blk_perm = block_perms[pos];
pos = hperm[pos];
//permute i-th block of support
for (j = 0; j < block_size; ++j)
sbl1[j] = support[j + i * block_size];
permutation::permute_dyadic (blk_perm, 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;
return 0;
}
int pubkey::encrypt (const bvector& in, bvector&out, prng&rng)
{
uint s = cipher_size(),
t = 1 << T;
if (t > s) return 1;
//create error vector
bvector e;
e.resize (s, 0);
for (uint n = t; n > 0;) {
uint p = rng.random (s);
if (!e[p]) {
e[p] = 1;
--n;
}
}
return encrypt (in, out, e);
}
int pubkey::encrypt (const bvector & in, bvector & out, const bvector&errors)
{
print_attack_warning();
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.append (cksum);
out.add (errors);
return 0;
}
int privkey::decrypt (const bvector & in, bvector & out)
{
bvector tmp_errors;
return decrypt (in, out, tmp_errors);
}
int privkey::decrypt (const bvector & in, bvector & out, bvector & errors)
{
print_attack_warning();
if (in.size() != cipher_size()) return 2;
polynomial synd;
uint i, 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_square //g(Li)^{-2}
(g.eval (permuted_support[i], fld));
fld.add_mults (tmp, permuted_support[i],
synd.begin(), synd.end());
}
//decoding
polynomial loc;
compute_alternant_error_locator (synd, fld, 1 << T, loc);
bool failed = false;
bvector ev;
if (!evaluate_error_locator_trace (loc, ev, fld))
failed = true;
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 || epos >= cipher_size()) {
//found unexpected/wrong support, die.
failed = true;
continue;
}
errors[epos] = 1;
if (epos < plain_size())
out[epos] = !out[epos];
}
return failed ? 1 : 0;
}

@ -1,104 +0,0 @@
/*
* This file is part of Codecrypt.
*
* Copyright (C) 2013-2016 Mirek Kratochvil <exa.exa@gmail.com>
*
* 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/>.
*/
#ifndef _ccr_mce_qd_h_
#define _ccr_mce_qd_h_
#include <vector>
#include "bvector.h"
#include "gf2m.h"
#include "matrix.h"
#include "permutation.h"
#include "polynomial.h"
#include "prng.h"
#include "sencode.h"
#include "types.h"
/*
* compact Quasi-dyadic McEliece
* according to Misoczki, Barreto, Compact McEliece Keys from Goppa Codes.
*
* Needs plaintext conversion. Unfortunately broken by an algebraic attack, do
* not use this.
*/
namespace mce_qd
{
class privkey
{
public:
std::vector<uint> essence;
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
std::vector<uint> block_perms; //dyadic permutations of blocks
permutation hperm; //block permutation of H block used to get G
//derivable stuff
//pre-permuted positions of support rows and support content
std::vector<uint> support_pos, permuted_support;
//generating polynomial
polynomial g;
int decrypt (const bvector&, bvector&);
int decrypt (const bvector&, bvector&, bvector&);
int prepare();
uint cipher_size() {
return (1 << T) * hperm.size();
}
uint plain_size() {
return (1 << T) * (hperm.size() - fld.m);
}
uint error_count() {
return 1 << T;
}
sencode* serialize();
bool unserialize (sencode*);
};
class pubkey
{
public:
uint T;
matrix qd_sigs;
int encrypt (const bvector&, bvector&, prng&);
int encrypt (const bvector&, bvector&, const bvector&);
uint cipher_size() {
return plain_size() + qd_sigs[0].size();
}
uint plain_size() {
return (1 << T) * qd_sigs.size();
}
uint error_count() {
return 1 << T;
}
sencode* serialize();
bool unserialize (sencode*);
};
int generate (pubkey&, privkey&, prng&, uint m, uint T, uint b, uint bd);
}
#endif

@ -1,216 +0,0 @@
/*
* This file is part of Codecrypt.
*
* Copyright (C) 2013-2016 Mirek Kratochvil <exa.exa@gmail.com>
*
* 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 "qd_utils.h"
#include <vector>
/*
* 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 (std::vector<int> x, std::vector<int>&r)
{
uint bs, s;
s = x.size();
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. 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;
}
bool qd_to_right_echelon_form (std::vector<std::vector<bvector> >&mat)
{
uint w = mat.size();
if (!w) return false;
uint h = mat[0].size();
if (!h) return false;
uint bs = mat[0][0].size();
uint i, j, k, l;
/*
* Inversion is done the quasi-dyadic way:
*
* - because for QD matrix m=delta(h) the product
* m*m = sum(h) * I, binary QD matrix m is either
* inversion of itself (m*m=I) or isn't invertible
* and m*m=0. sum(h), the "count of ones in QD
* signature mod 2", easily determines the result.
*
* - Using blockwise invertions/multiplications,
* gaussian elimination needed to invert the right
* square of H can be performed in O(m^2*block_count)
* matrix operations. Matrix operations are either
* addition (O(t) on QDs), multiplication(O(t log t)
* on QDs) or inversion (O(t), as shown above).
* Whole proces is therefore quite fast.
*
* Gaussian elimination on the QD signature should
* result in something like this: (for m=3, t=4)
*
* 1010 0101 1001 1000 0000 0000
* 0101 1100 1110 0000 1000 0000
* 0111 1110 0100 0000 0000 1000
*/
bvector tmp;
tmp.resize (bs);
std::vector<int> c1, c2, c3;
c1.resize (bs);
c2.resize (bs);
c3.resize (bs);
for (i = 0; i < h; ++i) { //gauss step
//first, find a nonsingular matrix in the column
for (j = i; j < h; ++j)
if (mat[w - h + i][j]
.hamming_weight() % 2) break;
if (j >= h) //none found, die!
return false;
//bring it to correct position (swap it to i-th row)
if (j > i) for (k = 0; k < w; ++k)
mat[k][i].swap
(mat[k][j]);
//now normalize the row
for (j = i; j < h; ++j) {
l = mat [w - h + i]
[j].hamming_weight();
if (l == 0) continue; //zero is just okay :]
if (! (l % 2)) //singular, make it regular by adding the i-th row
for (k = 0;
k < w;
++k)
mat[k][j].add
(mat[k][i]);
//now a matrix is regular, we can easily make it I.
//first, multiply the row
for (k = 0; k < w; ++k) {
//don't overwrite the matrix we're counting with
if (k == w - h + i) continue;
fwht_dyadic_multiply
(mat[w - h + i][j],
mat[k][j], tmp, c1, c2, c3);
mat[k][j] = tmp;
}
//change the block on the diagonal
fwht_dyadic_multiply
(mat[w - h + i][j],
mat[w - h + i][j], tmp, c1, c2, c3);
mat[w - h + i][j] = tmp;
//and zero the column below diagonal
if (j > i) for (k = 0; k < w; ++k)
mat[k][j].add
(mat[k][i]);
}
}
for (i = 0; i < h; ++i) { //jordan step
//normalize diagonal
for (k = 0; k < w - i; ++k) {
//we can safely rewrite the diagonal here (nothing's behind it)
fwht_dyadic_multiply
(mat[w - i - 1][h - i - 1],
mat[k][h - i - 1], tmp, c1, c2, c3);
mat[k][h - i - 1] = tmp;
}
//now make zeroes above
for (j = i + 1; j < h; ++j) {
l = mat[w - i - 1]
[h - j - 1].hamming_weight();
if (l == 0) continue; //already zero
if (! (l % 2)) { //nonsingular, fix it by adding diagonal
for (k = 0; k < w; ++k)
mat[k][h - j - 1].add
(mat[k][h - i - 1]);
}
for (k = 0; k < w - i; ++k) {
//overwrite is also safe here
fwht_dyadic_multiply
(mat[w - i - 1]
[h - j - 1],
mat[k][h - j - 1], tmp, c1, c2, c3);
mat[k][h - j - 1] = tmp;
}
//I+I=0
for (k = 0; k < w; ++k)
mat[k][h - j - 1].add
(mat[k][h - i - 1]);
}
}
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;
}
}

@ -25,7 +25,6 @@
#include "gf2m.h"
#include "polynomial.h"
#include "permutation.h"
#include "mce_qd.h"
#include "mce_qcmdpc.h"
#include "fmtseq.h"
#include "message.h"
@ -167,95 +166,6 @@ bool polynomial::unserialize (sencode* s)
#define PUBKEY_IDENT "CCR-PUBLIC-KEY-"
#define PRIVKEY_IDENT "CCR-PRIVATE-KEY-"
sencode* mce_qd::privkey::serialize()
{
sencode_list*l = new sencode_list;
l->items.resize (7);
l->items[0] = new sencode_bytes (PRIVKEY_IDENT "QD-MCE");
l->items[1] = fld.serialize();
l->items[2] = new sencode_int (T);
l->items[3] = serialize_uint_vector (&essence);
l->items[4] = block_perm.serialize();
l->items[5] = serialize_uint_vector (&block_perms);
l->items[6] = hperm.serialize();
return l;
}
bool mce_qd::privkey::unserialize (sencode* s)
{
sencode_list*CAST_LIST (s, l);
if (l->items.size() != 7) return false;
sencode_bytes*CAST_BYTES (l->items[0], ident);
if (ident->b.compare (PRIVKEY_IDENT "QD-MCE")) return false;
sencode_int*CAST_INT (l->items[2], p);
T = p->i;
if (! (fld.unserialize (l->items[1]) &&
unserialize_uint_vector (&essence, l->items[3]) &&
block_perm.unserialize (l->items[4]) &&
unserialize_uint_vector (&block_perms, l->items[5]) &&
hperm.unserialize (l->items[6]))) return false;
return true;
}
sencode* mce_qd::pubkey::serialize()
{
sencode_list*l = new sencode_list;
l->items.resize (3);
l->items[0] = new sencode_bytes (PUBKEY_IDENT "QD-MCE");
l->items[1] = new sencode_int (T);
l->items[2] = qd_sigs.serialize();
return l;
}
bool mce_qd::pubkey::unserialize (sencode*s)
{
sencode_list*CAST_LIST (s, l);
if (l->items.size() != 3) return false;
sencode_bytes*CAST_BYTES (l->items[0], ident);
if (ident->b.compare (PUBKEY_IDENT "QD-MCE")) return false;
sencode_int*CAST_INT (l->items[1], p);
T = p->i;
if (!qd_sigs.unserialize (l->items[2])) return false;
return true;
}
sencode* fmtseq::privkey::tree_stk_item::serialize()
{
sencode_list*l = new sencode_list;
l->items.resize (3);
l->items[0] = new sencode_int (level);
l->items[1] = new sencode_int (pos);
l->items[2] = new sencode_bytes (item);
return l;
}
bool fmtseq::privkey::tree_stk_item::unserialize (sencode*s)
{
sencode_list*CAST_LIST (s, l);
if (l->items.size() != 3) return false;
sencode_int*p;
CAST_INT (l->items[0], p);
level = p->i;
CAST_INT (l->items[1], p);
pos = p->i;
sencode_bytes* CAST_BYTES (l->items[2], a);
item = std::vector<byte> (a->b.begin(), a->b.end());
return true;
}
sencode* mce_qcmdpc::pubkey::serialize()
{
sencode_list*l = new sencode_list;
@ -316,6 +226,34 @@ bool mce_qcmdpc::privkey::unserialize (sencode*s)
return true;
}
sencode* fmtseq::privkey::tree_stk_item::serialize()
{
sencode_list*l = new sencode_list;
l->items.resize (3);
l->items[0] = new sencode_int (level);
l->items[1] = new sencode_int (pos);
l->items[2] = new sencode_bytes (item);
return l;
}
bool fmtseq::privkey::tree_stk_item::unserialize (sencode*s)
{
sencode_list*CAST_LIST (s, l);
if (l->items.size() != 3) return false;
sencode_int*p;
CAST_INT (l->items[0], p);
level = p->i;
CAST_INT (l->items[1], p);
pos = p->i;
sencode_bytes* CAST_BYTES (l->items[2], a);
item = std::vector<byte> (a->b.begin(), a->b.end());
return true;
}
sencode* fmtseq::privkey::serialize()
{