diff --git a/src/algorithm.h b/src/algorithm.h index 3d437db..2ba5378 100644 --- a/src/algorithm.h +++ b/src/algorithm.h @@ -62,7 +62,7 @@ public: } virtual int sign (const bvector&msg, bvector&sig, - sencode* privkey, bool&dirty, prng&rng) { + sencode** privkey, bool&dirty, prng&rng) { return -1; } diff --git a/src/algos_sig.cpp b/src/algos_sig.cpp index 1a6b24e..e77277a 100644 --- a/src/algos_sig.cpp +++ b/src/algos_sig.cpp @@ -16,7 +16,219 @@ * along with Codecrypt. If not, see . */ -#include "algos_enc.h" +#include "algos_sig.h" #include "fmtseq.h" +#include "sha_hash.h" +#include "rmd_hash.h" +#include "arcfour.h" + +/* + * DISCUSSION. + * + * Because the Merkle signatures "trapdoor" function isn't really trapdoor but + * plain uninvertible, every message in the scheme MUST have exactly one + * possible signature -- well there's no possibility to verify stuff + * nondeterministically. That completely sucks, because we can't get any + * coolness of "Probabilistic signature scheme" as known from RSA and others + * with inversion possibilities. + * + * That basically means that plaintexts MUST be prepared in the way that the + * signer knows they will never collide with anything he could want to sign (or + * especially NOT want to sign). Hopefully this is a common practice for + * digital signatures now. + * + * This scheme, apart from actual signature, protects against finding a + * low-length hash collision (which isn't very hard, assuming general + * availability of amounts of storage suitable for rainbow tables) by expanding + * the message to a reasonable minimum length prior to hashing. Algorithm is + * simple: + * + * 1. convert message from bvector to byte representation + * + * 2. if message is longer than the minimum length, forget padding :) + * + * 3. if it's shorter, use it as a seed for PRNG and pad it with PRNG output to + * minimum length. + * + * Then hash it as usual, FMTSeq it, publish the signature, enjoy. + */ + +#define min(a,b) ((a)<(b)?(a):(b)) + +static void msg_pad (const bvector&in, std::vector&out, size_t minsize) +{ + uint i; + + out.clear(); + out.resize ( ( (in.size() - 1) >> 3) + 1, 0); + for (i = 0; i < in.size(); ++i) + if (in[i]) out[i >> 3] |= 1 << (i & 0x7); + + if (out.size() >= minsize) return; + + arcfour g; + g.init (8); + + //stuff in as much seed material as possible + for (i = 0; i < (out.size() >> 8); ++i) { + std::vector sub (out.begin() + (i << 8), + min (out.end(), + out.begin() + ( (i + 1) << 8) ) ); + g.load_key (sub); + } + g.discard (256); + + i = out.size(); + out.resize (minsize); + for (; i < minsize; ++i) out[i] = g.gen(); +} + +/* + * actual signature stuff. + */ + +template +int fmtseq_generic_sign (const bvector&msg, + bvector&sig, + sencode**privkey, + bool&dirty, + prng&rng) +{ + //load the key + fmtseq::privkey Priv; + if (!Priv.unserialize (*privkey) ) return 1; + + //check parameters + if ( (Priv.h != h) || (Priv.l != l) + || (Priv.hs != hs) ) return 2; + + //prepare the message and hash it + std::vector M, H; + msg_pad (msg, M, hs); + message_hash msghf; + H = msghf (M); + + //convert to bvector + bvector hash; + hash.resize (hs, 0); + for (uint i = 0; i < hs; ++i) hash[i] = 1 & (H[i >> 3] >> (i & 0x7) ); + + //make a signature + tree_hash hf; + if (Priv.sign (hash, sig, hf) ) return 3; + + //if it went okay, refresh the privkey + sencode* new_pk = Priv.serialize(); + if (!new_pk) return 4; + sencode_destroy (*privkey); + *privkey = new_pk; + dirty = true; + + //all OK. + return 0; +} + +template +int fmtseq_generic_verify (const bvector&sig, + const bvector&msg, + sencode*pubkey) +{ + //load the key + fmtseq::pubkey Pub; + if (!Pub.unserialize (pubkey) ) return 1; + + //check parameters + if ( (Pub.H != h * l) || (Pub.hs != hs) ) return 2; + + //prepare the message and hash it + std::vector M, H; + msg_pad (msg, M, hs); + message_hash msghf; + H = msghf (M); + + //convert to bvector + bvector hash; + hash.resize (hs, 0); + for (uint i = 0; i < hs; ++i) hash[i] = 1 & (H[i >> 3] >> (i & 0x7) ); + + //check the signature + tree_hash hf; + if (Pub.verify (sig, hash, hf) ) return 3; + + //otherwise the sig is okay! + return 0; +} + +/* + * actual instantiations + */ + +int algo_fmtseq128::sign (const bvector&msg, + bvector&sig, + sencode**privkey, + bool&dirty, + prng&rng) +{ + return fmtseq_generic_sign + <4, 4, 256, sha256hash, rmd128hash> + (msg, sig, privkey, dirty, rng); +} + +int algo_fmtseq128::verify (const bvector&sig, + const bvector&msg, + sencode*pubkey) +{ + return fmtseq_generic_verify + <4, 4, 256, sha256hash, rmd128hash> + (sig, msg, pubkey); +} + +int algo_fmtseq256::sign (const bvector&msg, + bvector&sig, + sencode**privkey, + bool&dirty, + prng&rng) +{ + return fmtseq_generic_sign + <4, 4, 512, sha512hash, sha256hash> + (msg, sig, privkey, dirty, rng); +} + +int algo_fmtseq256::verify (const bvector&sig, + const bvector&msg, + sencode*pubkey) +{ + return fmtseq_generic_verify + <4, 4, 512, sha512hash, sha256hash> + (sig, msg, pubkey); +} + +int algo_fmtseq128::create_keypair (sencode**pub, sencode**priv, prng&rng) +{ + fmtseq::pubkey Pub; + fmtseq::privkey Priv; + + rmd128hash hf; + + if (fmtseq::generate (Pub, Priv, rng, hf, 256, 4, 4) ) + return 1; + + *pub = Pub.serialize(); + *priv = Priv.serialize(); +} + +int algo_fmtseq256::create_keypair (sencode**pub, sencode**priv, prng&rng) +{ + fmtseq::pubkey Pub; + fmtseq::privkey Priv; + + sha256hash hf; + + if (fmtseq::generate (Pub, Priv, rng, hf, 512, 4, 4) ) + return 1; + + *pub = Pub.serialize(); + *priv = Priv.serialize(); +} diff --git a/src/algos_sig.h b/src/algos_sig.h index e8c4be5..b6df71d 100644 --- a/src/algos_sig.h +++ b/src/algos_sig.h @@ -37,7 +37,7 @@ public: } virtual int sign (const bvector&msg, bvector&sig, - sencode* privkey, bool&dirty, prng&rng); + sencode** privkey, bool&dirty, prng&rng); virtual int verify (const bvector&sig, const bvector&msg, sencode* pubkey); int create_keypair (sencode**pub, sencode**priv, prng&rng); @@ -59,7 +59,7 @@ public: } virtual int sign (const bvector&msg, bvector&sig, - sencode* privkey, bool&dirty, prng&rng); + sencode** privkey, bool&dirty, prng&rng); virtual int verify (const bvector&sig, const bvector&msg, sencode* pubkey); int create_keypair (sencode**pub, sencode**priv, prng&rng); diff --git a/src/fmtseq.h b/src/fmtseq.h index 590f7d7..027ef14 100644 --- a/src/fmtseq.h +++ b/src/fmtseq.h @@ -81,7 +81,8 @@ public: } uint signature_size (hash_func&hf) { - return ( (h * l + fmtseq_commitments (hs) ) * hf.size() * 8) + (h * l); + return ( (h * l + fmtseq_commitments (hs) ) * hf.size() * 8) + + (h * l); } sencode* serialize(); diff --git a/src/message.cpp b/src/message.cpp index 5aedfac..8994b30 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -82,7 +82,7 @@ int signed_msg::sign (const bvector&msg, bool privkey_dirty = false; int r; - r = alg->sign (message, signature, privkey, privkey_dirty, rng); + r = alg->sign (message, signature, &privkey, privkey_dirty, rng); if (r) return r; @@ -90,7 +90,7 @@ int signed_msg::sign (const bvector&msg, kr.remove_privkey (key_id); //this actually shouldn't fail, key_id is not present kr.store_privkey (key_id, privkey); - //we can't output a signature without storing privkey changes + //we can't output a signature without storing privkey changes! if (!kr.disk_sync() ) return 3; } diff --git a/src/rmd_hash.h b/src/rmd_hash.h index 641a5e2..27df86a 100644 --- a/src/rmd_hash.h +++ b/src/rmd_hash.h @@ -17,8 +17,8 @@ * along with Codecrypt. If not, see . */ -#ifndef _sha_hash_h_ -#define _sha_hash_h_ +#ifndef _rmd_hash_h_ +#define _rmd_hash_h_ #include "hash.h" #include "ripemd128.h"