diff --git a/include/codecrypt.h b/include/codecrypt.h index 20acf21..e77ab77 100644 --- a/include/codecrypt.h +++ b/include/codecrypt.h @@ -43,17 +43,18 @@ public: void add (const bvector&); void add_range (const bvector&, uint, uint); void add_offset (const bvector&, uint); + void get_block (uint, uint, bvector&) const; bool operator* (const bvector&); //dot product bool zero() const; - void to_poly (polynomial&, gf2m&); + void to_poly (polynomial&, gf2m&) const; 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 colex_rank (bvector&); - void colex_unrank (bvector&, uint n, uint k); + void colex_rank (bvector&) const; + 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 uint T; //the QD's t parameter is 2^T. permutation block_perm; //order of blocks + //TODO this is derivable from hperm. uint block_count; //blocks >= block_count are shortened-out permutation hperm; //block permutation of H block used to get G std::vector block_perms; //dyadic permutations of blocks @@ -390,6 +392,8 @@ public: polynomial g; //computed goppa polynomial std::vector sqInv; + std::vector Hc; //signature lines of pre-permuted check matrix + std::vector support_pos; //pre-permuted positions of support rows int decrypt (const bvector&, bvector&); 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. * * 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. + * that achieves good speed, probability and non-exponential 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 impossible, as there is only an * extremely tiny probability of successful decoding. diff --git a/lib/bvector.cpp b/lib/bvector.cpp index b2556c3..3af9fe2 100644 --- a/lib/bvector.cpp +++ b/lib/bvector.cpp @@ -30,6 +30,13 @@ void bvector::add_offset (const bvector&a, uint offset) 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 r = 0; @@ -45,13 +52,13 @@ bool bvector::zero() const return true; } -void bvector::to_poly (polynomial&r, gf2m&fld) +void bvector::to_poly (polynomial&r, gf2m&fld) const { r.clear(); if (size() % fld.m) return; //impossible r.resize (size() / fld.m, 0); for (uint i = 0; i < size(); ++i) - if (item (i) ) r[i/fld.m] |= (1 << (i % fld.m) ); + if (item (i) ) r[i / fld.m] |= (1 << (i % fld.m) ); } void bvector::from_poly (const polynomial&r, gf2m&fld) @@ -59,17 +66,17 @@ void bvector::from_poly (const polynomial&r, gf2m&fld) clear(); resize (r.size() *fld.m, 0); for (uint i = 0; i < size(); ++i) - 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(); if (size() % fld.m) return; //impossible uint s = size() / fld.m; r.resize (s, 0); for (uint i = 0; i < size(); ++i) - if (item (i) ) r[i%s] |= (1 << (i / s) ); + if (item (i) ) r[i % s] |= (1 << (i / s) ); } void bvector::from_poly_cotrace (const polynomial&r, gf2m&fld) @@ -78,7 +85,7 @@ void bvector::from_poly_cotrace (const polynomial&r, gf2m&fld) uint s = r.size(); resize (s * fld.m, 0); for (uint i = 0; i < size(); ++i) - item (i) = (r[i%s] >> (i / s) ) & 1; + item (i) = (r[i % s] >> (i / s) ) & 1; } /* @@ -117,7 +124,7 @@ static void combination_number (uint n, uint k, mpz_t& r) 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_realloc2 (r, v.size() ); @@ -134,7 +141,7 @@ static void mpz_to_bvector (mpz_t&x, bvector&r) 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_init_set_ui (res, 0); @@ -158,8 +165,7 @@ void bvector::colex_rank (bvector&r) mpz_clear (res); } -#include -void bvector::colex_unrank (bvector&res, uint n, uint k) +void bvector::colex_unrank (bvector&res, uint n, uint k) const { mpz_t r, t, t2; mpz_init (r); @@ -185,7 +191,7 @@ void bvector::colex_unrank (bvector&res, uint n, uint k) mpz_swap (t2, r); mpz_sub (r, t2, t); if (p > n) continue; //overflow protection - res[p-1] = 1; + res[p - 1] = 1; } mpz_clear (r); diff --git a/lib/decoding.cpp b/lib/decoding.cpp index 3d70bb5..7d6c6f0 100644 --- a/lib/decoding.cpp +++ b/lib/decoding.cpp @@ -1,7 +1,7 @@ #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& sqInv, polynomial&out) { if (syndrome.zero() ) { @@ -11,9 +11,7 @@ void compute_error_locator (bvector&syndrome, gf2m&fld, polynomial& goppa, return; } - polynomial v; - syndrome.to_poly (v, fld); - + polynomial v = syndrome; v.inv (goppa, fld); // v=Synd^-1 mod goppa if (v.size() < 2) v.resize (2, 0); @@ -90,7 +88,7 @@ bool evaluate_error_locator_trace (polynomial&sigma, bvector&ev, gf2m&fld) 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] = trace_aux[i - 1]; trace_aux[i].square (fld); trace_aux[i].mod (sigma, fld); trace[0].add (trace_aux[i], fld); diff --git a/lib/decoding.h b/lib/decoding.h index 57eeba2..a88f5e1 100644 --- a/lib/decoding.h +++ b/lib/decoding.h @@ -6,7 +6,7 @@ using namespace ccr; -void compute_error_locator (bvector&syndrome, +void compute_error_locator (polynomial&syndrome, gf2m&fld, polynomial&goppa, std::vector& sqInv, diff --git a/lib/fwht.cpp b/lib/fwht.cpp index abeeb0b..0544bd5 100644 --- a/lib/fwht.cpp +++ b/lib/fwht.cpp @@ -21,9 +21,9 @@ static void fwht (vector x, vector&r) x.swap (r); for (uint i = 0; i < s; ++i) { if ( (i / bs) & 1) - r[i] = x[i-bs] - x[i]; + r[i] = x[i - bs] - x[i]; else - r[i] = x[i] + x[i+bs]; + r[i] = x[i] + x[i + bs]; } bs >>= 1; } diff --git a/lib/gf2m.cpp b/lib/gf2m.cpp index 1cba5ef..fc2b4fb 100644 --- a/lib/gf2m.cpp +++ b/lib/gf2m.cpp @@ -102,7 +102,7 @@ bool gf2m::create (uint M) log.resize (n, 0); antilog.resize (n, 0); log[0] = n - 1; - antilog[n-1] = 0; + antilog[n - 1] = 0; uint i, xi = 1; //x^0 for (i = 0; i < n - 1; ++i) { @@ -165,7 +165,7 @@ uint gf2m::exp (int k) uint gf2m::inv (uint a) { if (!a) return 0; - return antilog[ (n-1-log[a]) % (n - 1) ]; + return antilog[ (n - 1 - log[a]) % (n - 1) ]; } uint gf2m::sq_root (uint a) diff --git a/lib/matrix.cpp b/lib/matrix.cpp index 11e9253..4e9b64a 100644 --- a/lib/matrix.cpp +++ b/lib/matrix.cpp @@ -98,13 +98,13 @@ bool matrix::compute_inversion (matrix&res, bool upper_tri, bool lower_tri) if (upper_tri) { for (i = s; i > 0; --i) for (j = i - 1; j > 0; --j) - if (m[j-1][i-1]) - r[j-1].add_range (r[i-1], i - 1, s); + if (m[j - 1][i - 1]) + r[j - 1].add_range (r[i - 1], i - 1, s); } else { for (i = s; i > 0; --i) for (j = i - 1; j > 0; --j) - if (m[j-1][i-1]) - r[j-1].add (r[i-1]); + if (m[j - 1][i - 1]) + r[j - 1].add (r[i - 1]); } } @@ -214,7 +214,7 @@ void matrix::extend_left_compact (matrix&r) r[i][i] = 1; } for (i = 0; i < w; ++i) { - r[h+i] = item (i); + r[h + i] = item (i); } } @@ -233,6 +233,7 @@ bool matrix::create_goppa_generator (matrix&g, const permutation&p) t.get_right_square (sinv); if (!sinv.compute_inversion (s) ) return false; //meant to be retried. + //TODO why multiply and THEN strip? s.mult (t); s.strip_right_square (t); //matrix pingpong for the result t.compute_transpose (s); diff --git a/lib/mce.cpp b/lib/mce.cpp index c880902..adfd5e8 100644 --- a/lib/mce.cpp +++ b/lib/mce.cpp @@ -78,8 +78,9 @@ int privkey::decrypt (const bvector&in, bvector&out) h.mult_vec_right (canonical, syndrome); //decode - polynomial loc; - compute_error_locator (syndrome, fld, g, sqInv, loc); + polynomial synd, loc; + syndrome.to_poly (synd, fld); + compute_error_locator (synd, fld, g, sqInv, loc); bvector ev; 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; std::vector epos; permutation hpermInv; - polynomial loc; + polynomial loc, Synd; s = hash_size(); @@ -142,7 +143,8 @@ int privkey::sign (const bvector&in, bvector&out, uint delta, uint attempts, prn 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) ) { diff --git a/lib/mce_oc.cpp b/lib/mce_oc.cpp index cb9a0ee..9aa9c1b 100644 --- a/lib/mce_oc.cpp +++ b/lib/mce_oc.cpp @@ -74,7 +74,7 @@ int privkey::sign (const bvector&in, bvector&out, bvector e, e2, synd, synd_orig, cw, cwc, plain, overlap; std::vector epos; permutation hpermInv; - polynomial loc; + polynomial loc, Synd; uint i, t; uint mt = fld.m * codes[0].g.degree(), @@ -123,7 +123,8 @@ int privkey::sign (const bvector&in, bvector&out, e[epos[i]] = 1; } - compute_error_locator (synd, fld, + synd.to_poly (Synd, fld); + compute_error_locator (Synd, fld, codes[ci].g, codes[ci].sqInv, loc); diff --git a/lib/mce_qd.cpp b/lib/mce_qd.cpp index 493a677..aec5648 100644 --- a/lib/mce_qd.cpp +++ b/lib/mce_qd.cpp @@ -5,6 +5,7 @@ using namespace ccr; using namespace ccr::mce_qd; #include "decoding.h" +#include "fwht.h" #include @@ -37,6 +38,7 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, priv.T = T; uint t = 1 << T; + std::cout << "generate" << std::endl; //convenience gf2m&fld = priv.fld; std::vector&Hsig = priv.Hsig; @@ -56,37 +58,39 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, std::set used; used.clear(); + std::cout << "attempt..." << std::endl; //first off, compute the H signature Hsig[0] = choose_random (fld.n, rng, used); - essence[m-1] = fld.inv (Hsig[0]); + essence[m - 1] = fld.inv (Hsig[0]); //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 Hsig[i] = choose_random (fld.n, rng, used); - essence[s] = fld.add (essence[m-1], fld.inv (Hsig[i]) ); + essence[s] = fld.add (essence[m - 1], fld.inv (Hsig[i]) ); 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] - ) ) ); - used.insert (Hsig[i+j]); + 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]) ) ); + (fld.inv (Hsig[i + j]), + essence[m - 1]) ) ); } } //from now on, we fix 'omega' from the paper to zero. + std::cout << "goppa..." << std::endl; //assemble goppa polynomial. g.clear(); g.resize (1, 1); //g(x)=1 so we can multiply it @@ -96,15 +100,19 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, //tmp(x)=x-z=x-(1/h_i) tmp[0] = fld.inv (Hsig[i]); 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. used.clear(); bool consistent = true; for (uint i = 0; i < fld.n / 2; ++i) { support[i] = fld.add ( fld.inv (Hsig[i]), - essence[m-1]); + essence[m - 1]); if (used.count (support[i]) ) { consistent = false; @@ -112,15 +120,18 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, } if (g.eval (support[i], fld) == 0) { + std::cout << "support zero!" << std::endl; consistent = false; break; } + std::cout << "support at " << i << ": " << support[i] << std::endl; used.insert (support[i]); } if (!consistent) continue; //retry + std::cout << "blocks..." << std::endl; //now the blocks. uint block_size = 1 << T, 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 + 1) * block_size); + std::cout << "permuting blocks..." << std::endl; //permute them priv.block_perm.generate_random (h_block_count, rng); priv.block_perm.permute (bl, blp); + std::cout << "discarding blocks..." << std::endl; //discard blocks blp.resize (block_count); + std::cout << "permuting dyadic blocks..." << std::endl; //permute individual blocks priv.block_perms.resize (block_count); bl.resize (blp.size() ); @@ -158,14 +172,17 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, 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); for (uint i = 0; i < block_count; ++i) for (uint j = 0; j < block_size; ++j) { permutation::permute_dyadic (j, bl[priv.hperm[i]], col); - Hc[i*block_size + j].from_poly_cotrace + Hc[i * block_size + j].from_poly_cotrace (col, fld); } @@ -177,22 +194,29 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, */ Hc.get_right_square (r); + std::cout << "RIGHT SQUARE " << r; if (!r.compute_inversion (ri) ) - continue; //retry with other hperm + continue; //retry with other code + std::cout << "Rinv " << ri; Hc.strip_right_square (l); ri.mult (l); + std::cout << "l " << ri; break; } + if (attempts == block_count) //generating G failed, retry all + continue; + /* * Redundancy-checking part of G is now (transposed) in ri. * Get QD signatures by getting every t'th row (transposed). */ + std::cout << "pubkey..." << std::endl; pub.T = T; pub.qd_sigs.resize (ri.width() / t); for (uint i = 0; i < ri.width(); i += t) - pub.qd_sigs[i/t] = ri[i]; + pub.qd_sigs[i / t] = ri[i]; return 0; } @@ -200,23 +224,23 @@ int mce_qd::generate (pubkey&pub, privkey&priv, prng&rng, int privkey::prepare() { + std::cout << "prepare" << std::endl; //compute H signature from essence 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) { 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) - Hsig[i+j] = fld.inv - (fld.add - (fld.inv (Hsig[i]), - fld.add ( - fld.inv (Hsig[j]), - essence[fld.m-1] - ) ) ); + Hsig[i + j] = fld.inv + (fld.add + (fld.inv (Hsig[i]), + fld.add ( + fld.inv (Hsig[j]), + essence[fld.m - 1] + ) ) ); } //compute the support @@ -224,10 +248,15 @@ int privkey::prepare() for (uint i = 0; i < fld.n / 2; ++i) { support[i] = fld.add (fld.inv (Hsig[i]), - essence[fld.m-1]); + essence[fld.m - 1]); } + //TODO prepare permuted Hsig (that can be applied to the ciphertext) + + //TODO prepare function that converts a support zero to ciphertext + //position + //goppa polynomial g.clear(); g.resize (1, 1); @@ -245,12 +274,11 @@ int privkey::prepare() return 0; } -#include "fwht.h" - int pubkey::encrypt (const bvector& in, bvector&out, prng&rng) { uint t = 1 << T; bvector p, g, r, cksum; + uint i, j, k; /* * 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); r.resize (t); - for (uint i = 0; i < qd_sigs.size(); ++i) { - //plaintext block - for (uint k = 0; k < t; ++k) p[k] = in[k+i*t]; + for (i = 0; i < qd_sigs.size(); ++i) { + std::cout << "Signature line " << i << ": " << qd_sigs[i]; + } - 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 - 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 fwht_dyadic_multiply (p, g, r); + //std::cout << "DYADIC MULTIPLY: " << p << g << r << "---" << std::endl; 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 out = in; out.insert (out.end(), cksum.begin(), cksum.end() ); + std::cout << "without errors: " << out; out.add (e); return 0; @@ -303,7 +338,45 @@ int pubkey::encrypt (const bvector& in, bvector&out, prng&rng) 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; } diff --git a/lib/nd.cpp b/lib/nd.cpp index de3116d..41fe8ba 100644 --- a/lib/nd.cpp +++ b/lib/nd.cpp @@ -57,8 +57,9 @@ int privkey::decrypt (const bvector&in, bvector&out) bvector unsc; //unscrambled Sinv.mult_vec_right (in, unsc); - polynomial loc; - compute_error_locator (unsc, fld, g, sqInv, loc); + polynomial loc, synd; + unsc.to_poly (synd, fld); + compute_error_locator (synd, fld, g, sqInv, loc); bvector ev; 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; bvector synd_unsc, synd, e; - - polynomial loc; + polynomial loc, Synd; s = hash_size(); 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); - 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) ) { diff --git a/lib/polynomial.cpp b/lib/polynomial.cpp index d88d4cc..3d0ab79 100644 --- a/lib/polynomial.cpp +++ b/lib/polynomial.cpp @@ -58,8 +58,8 @@ void polynomial::mod (const polynomial&f, gf2m&fld) for (int i = 0; i <= df; ++i) item (i + d - df) - = fld.add (item (i + d - df), - fld.mult (t, f[i]) ); + = fld.add (item (i + d - df), + fld.mult (t, f[i]) ); } strip(); } @@ -326,7 +326,7 @@ void polynomial::divmod (polynomial&d, polynomial&res, polynomial&rem, gf2m&fld) if (res.size() < rp + 1) res.resize (rp + 1, 0); res[rp] = fld.mult (headInv, rem[t]); for (uint i = 0; i <= degd; ++i) - rem[i+rp] = fld.add (rem[i+rp], fld.mult (res[rp], d[i]) ); + rem[i + rp] = fld.add (rem[i + rp], fld.mult (res[rp], d[i]) ); } rem.strip(); }