fmtseq: working (tbd: tree cache updates)

This commit is contained in:
Mirek Kratochvil 2012-12-27 19:49:21 +01:00
parent 42d8ddbd07
commit 902a2f541d
5 changed files with 298 additions and 39 deletions

View file

@ -19,14 +19,16 @@
#include "fmtseq.h"
#include "arcfour.h"
#include <iostream>
using namespace fmtseq;
void prepare_keygen (arcfour<char>& kg, const std::vector<char>&SK, uint idx)
void prepare_keygen (arcfour<byte>& kg, const std::vector<byte>&SK, uint idx)
{
kg.clear();
kg.init (8);
kg.load_key (SK);
std::vector<char>tmp;
std::vector<byte>tmp;
while (idx) {
tmp.push_back (idx & 0xff);
idx >>= 8;
@ -35,25 +37,57 @@ void prepare_keygen (arcfour<char>& kg, const std::vector<char>&SK, uint idx)
kg.load_key (tmp);
}
//don't feed zero
static uint log2 (uint x)
static void add_zero_checksum (bvector& v)
{
uint r = 0;
while (x) {
++r;
x >>= 1;
uint s = v.size();
if (!s) return;
uint z = s - v.hamming_weight(); //0's instead of 1's
v.resize (fmtseq_commitments (s) );
while (z) {
v[s] = z & 1;
z >>= 1;
++s;
}
return r - 1;
}
static void alloc_exist (privkey&priv)
{
priv.exist.resize (priv.l);
uint ts = (1 << (priv.h + 1) ) - 2;
for (uint i = 0; i < priv.l; ++i)
priv.exist[i].resize (ts);
}
static void store_exist (privkey&priv, const privkey::tree_stk_item&i)
{
uint level = i.level / priv.h;
if (level >= priv.l) return; //top node
uint sublevel = priv.h - (i.level % priv.h);
if (i.pos >= (1 << sublevel) ) return; //too far right
priv.exist[level][i.pos + (1 << sublevel) - 2] = i.item;
}
static void update_trees (privkey&priv, hash_func&hf)
{
//TODO
}
int fmtseq::generate (pubkey&pub, privkey&priv,
prng&rng, hash_func&hf,
uint h, uint l)
uint hs, uint h, uint l)
{
uint i, j;
//first off, generate a secret key for commitment generator.
/*
* first off, generate a secret key for commitment generator.
* exactly THIS gives the amount of all possible FMTseq privkeys.
*
* in our case it's around 2^2048, which is Enough.
*/
priv.SK.resize (1 << 8);
for (i = 0; i < (1 << 8); ++i) {
priv.SK[i] = rng.random (1 << 8);
@ -61,22 +95,24 @@ int fmtseq::generate (pubkey&pub, privkey&priv,
priv.h = h;
priv.l = l;
priv.hs = hs;
priv.sigs_used = 0;
std::vector<privkey::tree_stk_item> stk;
stk.reserve (h * l + 1);
uint sigs = 1 << (h * l);
//number of commitments needed for signature (bits+log2(bits))
uint commitments = 8 * hf.size();
commitments += log2 (commitments);
uint commitments = fmtseq_commitments (hs);
arcfour<char> generator;
std::vector<char> x, y, Y;
arcfour<byte> generator;
std::vector<byte> x, y, Y;
x.resize (hf.size() );
y.resize (hf.size() );
alloc_exist (priv);
for (i = 0; i < sigs; ++i) {
//generate commitments and concat publics into Y
Y.clear();
@ -88,8 +124,10 @@ int fmtseq::generate (pubkey&pub, privkey&priv,
Y.insert (Y.end(), y.begin(), y.end() );
}
stk.push_back (privkey::tree_stk_item (0, hf (Y) ) );
stk.push_back (privkey::tree_stk_item (0, i, hf (Y) ) );
store_exist (priv, stk.back() );
//try squashing the stack
for (;;) {
if (stk.size() < 2) break;
if ( (stk.end() - 1)->level != (stk.end() - 2)->level) break;
@ -102,16 +140,163 @@ int fmtseq::generate (pubkey&pub, privkey&priv,
(stk.end() - 1)->item.begin(),
(stk.end() - 1)->item.end() );
uint l = stk.back().level + 1;
uint p = stk.back().pos / 2;
stk.pop_back();
stk.pop_back();
stk.push_back (privkey::tree_stk_item (l, hf (Y) ) );
stk.push_back (privkey::tree_stk_item (l, p, hf (Y) ) );
store_exist (priv, stk.back() );
}
}
//now there's the public verification key available in the stack.
pub.check = stk.back().item;
pub.H = h * l;
pub.hs = hs;
return 0;
}
/*
* SIGNATURE STRUCTURE
* is variable, but following stuff is concatenated exactly in this order:
*
* - private/public commitments (less than size+log2(size) in bits times hash
* size) in the "natural" left-to-right order (or from first to last bits of
* hash. Checksum goes last, with least significant bit first). Private
* commitment goes whenever there's 1 in message, public on 0.
* - h*l hashes of verification chain, from bottom to top, h*l times hash size
* - i (so that we can guess left/right concatenation before hashing) stored as
* H-bit number in little endian.
* TODO Why cannot the tree be just XORed together? would be WAY simpler!
*
* summed up:
*
* Sig=(x0, y1, x2, x3, y4, ..... , xComm-1,path0,path1,...,pathH-1, i)
*
*/
#include "ios.h"
int privkey::sign (const bvector& hash, bvector& sig, hash_func& hf)
{
if (hash.size() != hash_size() ) return 2;
if (!sigs_remaining() ) return 2;
uint commitments = fmtseq_commitments (hs);
bvector M2 = hash;
add_zero_checksum (M2);
std::vector<byte> Sig, t;
uint i;
Sig.reserve (hf.size() * (commitments + h * l) );
//first, compute the commitments and push them to the signature
arcfour<byte> generator;
prepare_keygen (generator, SK, sigs_used);
for (i = 0; i < commitments; ++i) {
//generate x_i
generator.gen (hf.size(), t);
//if it's 0, publish y_i, else publish x_i
if (!M2[i]) t = hf (t);
//append it to signature
Sig.insert (Sig.end(), t.begin(), t.end() );
}
//now retrieve the authentication path
uint pos = sigs_used;
uint exlev, expos, exid;
for (i = 0; i < h * l; ++i) {
exid = i / h;
exlev = h - (i % h);
//flip the last bit of pos so it gets the neighbor
expos = (pos ^ 1) % (1 << exlev);
Sig.insert (Sig.end(),
exist[exid][expos + (1 << exlev) - 2].begin(),
exist[exid][expos + (1 << exlev) - 2].end() );
pos >>= 1;
}
//prepare the signature
sig.clear();
sig.resize (signature_size (hf), 0);
//convert to bits
uint sig_no_start = (commitments + h * l) * hf.size() * 8;
for (i = 0; i < sig_no_start; ++i)
sig[i] = 1 & (Sig[i / 8] >> (i % 8) );
//append signature number
pos = sigs_used;
for (i = 0; i < h * l; ++i) {
sig[i + sig_no_start] = pos & 1;
pos >>= 1;
}
//move on to the next signature
++sigs_used;
//update the cache
update_trees (*this, hf);
return 0;
}
int pubkey::verify (const bvector& sig, const bvector& hash, hash_func& hf)
{
uint i, j;
if (sig.size() != signature_size (hf) ) return 2;
if (hash.size() != hash_size() ) return 2;
uint commitments = fmtseq_commitments (hs);
bvector M2 = hash;
add_zero_checksum (M2);
if (M2.size() != commitments) return 3; //likely internal failure
//retrieve i
uint sig_no = 0;
for (i = sig.size() - 1; i >= (commitments + H) * hf.size() * 8; --i)
sig_no = (sig_no << 1) + (sig[i] ? 1 : 0);
std::vector<byte> t, Y;
std::vector<std::vector<byte> > Sig;
//split and convert to byte form for convenient hashing
Sig.resize (commitments + H);
for (i = 0; i < (commitments + H); ++i) {
Sig[i].resize (hf.size(), 0);
for (j = 0; j < hf.size() * 8; ++j)
if (sig[j + i * hf.size() * 8])
Sig[i][j / 8] |= (1 << (j % 8) );
}
Y.clear();
for (i = 0; i < commitments; ++i) {
if (M2[i]) t = hf (Sig[i]); //convert pk_i to sk_i at 1's
else t = Sig[i]; //else it should already be pk_i
Y.insert (Y.end(), t.begin(), t.end() ); //append it to Y_i
}
//create the leaf
t = hf (Y);
//walk the authentication path
for (i = 0; i < H; ++i) {
Y.clear();
Y = Sig[commitments + i];
if ( (sig_no >> i) & 1) {
//append path auth from left
Y.insert (Y.end(), t.begin(), t.end() );
t = hf (Y);
} else {
//append from right
t.insert (t.end(), Y.begin(), Y.end() );
t = hf (t);
}
}
if (t == check) return 0; //all went okay
else return 1;
}

View file

@ -34,27 +34,38 @@
namespace fmtseq
{
//helper function used to calculate hash sizes percisely
inline uint fmtseq_commitments (uint l)
{
uint x = l;
while (x) {
++l;
x >>= 1;
}
return l;
}
class privkey
{
public:
std::vector<char> SK; //secret key
std::vector<byte> SK; //secret key
uint h, l; //l=level count, h=level height (root-leaf path length)
//therefore, H = h*l
uint sigs_used;
uint hs;
//FMT caches
std::vector<std::vector<char> > exist;
std::vector<std::vector<char> > desired;
std::vector<std::vector<std::vector<byte> > > exist, desired;
struct tree_stk_item {
uint level;
std::vector<char> item;
uint level, pos;
std::vector<byte> item;
tree_stk_item() {}
tree_stk_item (uint L, std::vector<char> i)
: level (L), item (i) {}
tree_stk_item (uint L, uint P, std::vector<byte> i)
: level (L), pos (P), item (i) {}
};
std::vector<std::list<tree_stk_item> > desired_stack;
std::vector<uint> desired_progress;
int sign (const bvector&, bvector&, hash_func&);
@ -62,8 +73,12 @@ public:
return (1 << (h * l) ) - sigs_used;
}
uint hash_size (hash_func&hf) {
return hf.size() * 8;
uint hash_size () {
return hs;
}
uint signature_size (hash_func&hf) {
return ( (h * l + fmtseq_commitments (hs) ) * hf.size() * 8) + (h * l);
}
sencode* serialize();
@ -73,20 +88,25 @@ public:
class pubkey
{
public:
std::vector<char> check; //tree top verification hash
uint H;
std::vector<byte> check; //tree top verification hash
uint H, hs;
int verify (const bvector&, const bvector&, hash_func&);
uint hash_size (hash_func&hf) {
return hf.size() * 8;
uint hash_size () {
return hs;
}
uint signature_size (hash_func&hf) {
return ( (H + fmtseq_commitments (hs) ) * hf.size() * 8) + H;
}
sencode* serialize();
bool unserialize (sencode*);
};
int generate (pubkey&, privkey&, prng&, hash_func&, uint h, uint l);
int generate (pubkey&, privkey&, prng&, hash_func&, uint hs, uint h, uint l);
}
#endif

View file

@ -28,8 +28,8 @@
class hash_func
{
public:
virtual std::vector<char> operator() (const std::vector<char>&) = 0;
virtual uint size(); //in bytes
virtual std::vector<byte> operator() (const std::vector<byte>&) = 0;
virtual uint size() = 0; //in bytes
};
#endif

View file

@ -16,13 +16,13 @@
* along with Codecrypt. If not, see <http://www.gnu.org/licenses/>.
*/
#include "arcfour.h"
#include "prng.h"
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include "ios.h"
#include <iomanip>
using namespace std;
@ -38,8 +38,62 @@ public:
}
};
#include "hash.h"
#include "sha2.h"
#include <stdint.h>
class sha2hash : public hash_func
{
public:
uint size() {
//return 4;
return SHA256_DIGEST_LENGTH;
}
vector<byte> operator() (const vector<byte>&a) {
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, (const uint8_t*) & (a[0]), a.size() );
vector<byte> r;
r.resize (size() );
//r.resize (SHA256_DIGEST_LENGTH);
SHA256_Final ( (uint8_t*) & (r[0]), &ctx);
//r.resize(size());
return r;
}
};
#include "fmtseq.h"
#include "arcfour.h"
int main()
{
primitiverng r;
r.seed (0);
sha2hash sha2;
fmtseq::privkey priv;
fmtseq::pubkey pub;
cout << fmtseq::generate (pub, priv, r, sha2, 128, 3, 3) << endl;
bvector h, sig;
h.resize (priv.hash_size(), 0);
h[0] = 1;
h[1] = 1;
//for (uint i = 0; i < 10; ++i) h[2 * i] = 1;
cout << "HASH " << h;
for (uint i = 0; i < 8; ++i) {
cout << priv.sign (h, sig, sha2) << endl;
//cout << i << "-th SIG " << sig;
cout << "VERIFY ERROR: " << pub.verify (sig, h, sha2) << endl;
}
return 0;
#if 0
arcfour<unsigned short> c;
if (!c.init (10) ) {
cout << "haha." << endl;
@ -59,7 +113,6 @@ int main()
return 0;
#if 0
primitiverng r;
r.seed (0);

View file

@ -26,6 +26,7 @@
* adviseable when computing with m=16 and larger.
*/
typedef unsigned int uint;
typedef unsigned char byte;
//TODO add separate type for GF(2^m) elements!