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"