initial import of blind_id
authorRudolf Polzer <divverent@alientrap.org>
Sat, 24 Apr 2010 18:11:20 +0000 (20:11 +0200)
committerRudolf Polzer <divverent@alientrap.org>
Sat, 24 Apr 2010 18:11:20 +0000 (20:11 +0200)
12 files changed:
Makefile [new file with mode: 0644]
d0.c [new file with mode: 0644]
d0.h [new file with mode: 0644]
d0_bignum-gmp.c [new file with mode: 0644]
d0_bignum.h [new file with mode: 0644]
d0_blind_id.c [new file with mode: 0644]
d0_blind_id.h [new file with mode: 0644]
d0_iobuf.c [new file with mode: 0644]
d0_iobuf.h [new file with mode: 0644]
main.c [new file with mode: 0644]
sha1.c [new file with mode: 0644]
sha1.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8534658
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+all: blind_id
+
+OBJECTS = d0.o d0_blind_id.o d0_iobuf.o d0_bignum-gmp.o sha1.o main.o
+
+blind_id: $(OBJECTS)
+       $(CC) $(LDFLAGS) -o $@ $^
+clean:
+       $(RM) blind_id $(OBJECTS)
+
+CFLAGS += -Wall -Wextra
+CPPFLAGS += -I/opt/gmp/include
+LDFLAGS += -L/opt/gmp/lib -Wl,-rpath,/opt/gmp/lib -lgmp -lm -lrt -s -O3
diff --git a/d0.c b/d0.c
new file mode 100644 (file)
index 0000000..fd61cdb
--- /dev/null
+++ b/d0.c
@@ -0,0 +1,6 @@
+#include "d0.h"
+
+#include <stdlib.h>
+
+void *(*d0_malloc)(size_t len) = malloc;
+void (*d0_free)(void *p) = free;
diff --git a/d0.h b/d0.h
new file mode 100644 (file)
index 0000000..c792430
--- /dev/null
+++ b/d0.h
@@ -0,0 +1,12 @@
+#ifndef __D0_H__
+#define __D0_H__
+
+#include <stdlib.h> // size_t
+
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define BOOL int
+
+extern void *(*d0_malloc)(size_t len);
+extern void (*d0_free)(void *p);
+
+#endif
diff --git a/d0_bignum-gmp.c b/d0_bignum-gmp.c
new file mode 100644 (file)
index 0000000..5cd31c0
--- /dev/null
@@ -0,0 +1,249 @@
+#include "d0_bignum.h"
+
+#include <gmp.h>
+
+struct d0_bignum_s
+{
+       mpz_t z;
+};
+
+static gmp_randstate_t RANDSTATE;
+
+#include <time.h>
+void d0_bignum_INITIALIZE()
+{
+       gmp_randinit_default(RANDSTATE);
+       gmp_randseed_ui(RANDSTATE, time(NULL)); // TODO seed
+}
+
+void d0_bignum_SHUTDOWN()
+{
+       // free RANDSTATE
+}
+
+BOOL d0_iobuf_write_bignum(d0_iobuf_t *buf, const d0_bignum_t *bignum)
+{
+       static unsigned char numbuf[65536];
+       size_t count = 0;
+       numbuf[0] = mpz_sgn(bignum->z) & 3;
+       if((numbuf[0] & 3) != 0) // nonzero
+       {
+               count = (mpz_sizeinbase(bignum->z, 2) + 7) / 8;
+               if(count > sizeof(numbuf) - 1)
+                       return 0;
+               mpz_export(numbuf+1, &count, 1, 1, 0, 0, bignum->z);
+       }
+       return d0_iobuf_write_packet(buf, numbuf, count + 1);
+}
+
+d0_bignum_t *d0_iobuf_read_bignum(d0_iobuf_t *buf, d0_bignum_t *bignum)
+{
+       static unsigned char numbuf[65536];
+       size_t count = sizeof(numbuf);
+       if(!d0_iobuf_read_packet(buf, numbuf, &count))
+               return NULL;
+       if(count < 1)
+               return NULL;
+       if(!bignum) bignum = d0_bignum_new(); if(!bignum) return NULL;
+       if(numbuf[0] & 3) // nonzero
+       {
+               mpz_import(bignum->z, count-1, 1, 1, 0, 0, numbuf+1);
+               if(numbuf[0] & 2) // negative
+                       mpz_neg(bignum->z, bignum->z);
+       }
+       else // zero
+       {
+               mpz_set_ui(bignum->z, 0);
+       }
+       return bignum;
+}
+
+d0_bignum_t *d0_bignum_new()
+{
+       d0_bignum_t *b = d0_malloc(sizeof(d0_bignum_t));
+       mpz_init(b->z);
+       return b;
+}
+
+void d0_bignum_free(d0_bignum_t *a)
+{
+       mpz_clear(a->z);
+       d0_free(a);
+}
+
+void d0_bignum_init(d0_bignum_t *b)
+{
+       mpz_init(b->z);
+}
+
+void d0_bignum_clear(d0_bignum_t *a)
+{
+       mpz_clear(a->z);
+}
+
+size_t d0_bignum_size(const d0_bignum_t *r)
+{
+       return mpz_sizeinbase(r->z, 2);
+}
+
+int d0_bignum_cmp(const d0_bignum_t *a, const d0_bignum_t *b)
+{
+       return mpz_cmp(a->z, b->z);
+}
+
+d0_bignum_t *d0_bignum_rand_range(d0_bignum_t *r, const d0_bignum_t *min, const d0_bignum_t *max)
+{
+       static d0_bignum_t *temp = NULL; if(!temp) temp = d0_bignum_new();
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_sub(temp->z, max->z, min->z);
+       mpz_urandomm(r->z, RANDSTATE, temp->z);
+       mpz_add(r->z, r->z, min->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_rand_bit_atmost(d0_bignum_t *r, size_t n)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_urandomb(r->z, RANDSTATE, n);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_rand_bit_exact(d0_bignum_t *r, size_t n)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_urandomb(r->z, RANDSTATE, n-1);
+       mpz_setbit(r->z, n-1);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_zero(d0_bignum_t *r)
+{
+       return d0_bignum_int(r, 0);
+}
+
+d0_bignum_t *d0_bignum_one(d0_bignum_t *r)
+{
+       return d0_bignum_int(r, 1);
+}
+
+d0_bignum_t *d0_bignum_int(d0_bignum_t *r, int n)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_set_si(r->z, n);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_mov(d0_bignum_t *r, const d0_bignum_t *a)
+{
+       if(r == a)
+               return r; // trivial
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_set(r->z, a->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_neg(d0_bignum_t *r, const d0_bignum_t *a)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_neg(r->z, a->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_shl(d0_bignum_t *r, const d0_bignum_t *a, ssize_t n)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       if(n > 0)
+               mpz_mul_2exp(r->z, a->z, n);
+       else if(n < 0)
+               mpz_fdiv_q_2exp(r->z, a->z, -n);
+       else
+               mpz_set(r->z, a->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_add(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_add(r->z, a->z, b->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_sub(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_sub(r->z, a->z, b->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_mul(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_mul(r->z, a->z, b->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_divmod(d0_bignum_t *q, d0_bignum_t *m, const d0_bignum_t *a, const d0_bignum_t *b)
+{
+       if(!q && !m)
+               m = d0_bignum_new();
+       if(q)
+               if(m)
+                       mpz_fdiv_qr(q->z, m->z, a->z, b->z);
+               else
+                       mpz_fdiv_q(q->z, a->z, b->z);
+       else
+               mpz_fdiv_r(m->z, a->z, b->z);
+       if(m)
+               return m;
+       else
+               return q;
+}
+
+d0_bignum_t *d0_bignum_mod_add(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
+{
+       r = d0_bignum_add(r, a, b);
+       mpz_fdiv_r(r->z, r->z, m->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_mod_mul(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
+{
+       r = d0_bignum_mul(r, a, b);
+       mpz_fdiv_r(r->z, r->z, m->z);
+       return r;
+}
+
+d0_bignum_t *d0_bignum_mod_pow(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       mpz_powm(r->z, a->z, b->z, m->z);
+       return r;
+}
+
+BOOL d0_bignum_mod_inv(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *m)
+{
+       // here, r MUST be set, as otherwise we cannot return error state!
+       return mpz_invert(r->z, a->z, m->z);
+}
+
+int d0_bignum_isprime(d0_bignum_t *r, int param)
+{
+       return mpz_probab_prime_p(r->z, param);
+}
+
+d0_bignum_t *d0_bignum_gcd(d0_bignum_t *r, d0_bignum_t *s, d0_bignum_t *t, const d0_bignum_t *a, const d0_bignum_t *b)
+{
+       if(!r) r = d0_bignum_new(); if(!r) return NULL;
+       if(s)
+               mpz_gcdext(r->z, s->z, t ? t->z : NULL, a->z, b->z);
+       else if(t)
+               mpz_gcdext(r->z, t->z, NULL, b->z, a->z);
+       else
+               mpz_gcd(r->z, a->z, b->z);
+       return r;
+}
+
+char *d0_bignum_tostring(const d0_bignum_t *x, unsigned int base)
+{
+       return mpz_get_str(NULL, base, x->z);
+}
diff --git a/d0_bignum.h b/d0_bignum.h
new file mode 100644 (file)
index 0000000..7eb0e0a
--- /dev/null
@@ -0,0 +1,38 @@
+#include "d0.h"
+#include "d0_iobuf.h"
+
+typedef struct d0_bignum_s d0_bignum_t;
+
+WARN_UNUSED_RESULT BOOL d0_iobuf_write_bignum(d0_iobuf_t *buf, const d0_bignum_t *bignum);
+WARN_UNUSED_RESULT d0_bignum_t *d0_iobuf_read_bignum(d0_iobuf_t *buf, d0_bignum_t *bignum);
+
+void d0_bignum_INITIALIZE();
+void d0_bignum_SHUTDOWN();
+
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_new();
+void d0_bignum_free(d0_bignum_t *a);
+void d0_bignum_init(d0_bignum_t *b);
+void d0_bignum_clear(d0_bignum_t *a);
+WARN_UNUSED_RESULT size_t d0_bignum_size(const d0_bignum_t *r);
+WARN_UNUSED_RESULT int d0_bignum_cmp(const d0_bignum_t *a, const d0_bignum_t *b);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_rand_range(d0_bignum_t *r, const d0_bignum_t *min, const d0_bignum_t *max);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_rand_bit_atmost(d0_bignum_t *r, size_t n);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_rand_bit_exact(d0_bignum_t *r, size_t n);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_zero(d0_bignum_t *r);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_one(d0_bignum_t *r);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_int(d0_bignum_t *r, int n);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_mov(d0_bignum_t *r, const d0_bignum_t *a);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_neg(d0_bignum_t *r, const d0_bignum_t *a);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_shl(d0_bignum_t *r, const d0_bignum_t *a, ssize_t n);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_add(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_sub(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_mul(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_divmod(d0_bignum_t *q, d0_bignum_t *m, const d0_bignum_t *a, const d0_bignum_t *b); // only do mod if both are NULL
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_mod_add(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_mod_mul(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_mod_pow(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m);
+WARN_UNUSED_RESULT BOOL d0_bignum_mod_inv(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *m);
+WARN_UNUSED_RESULT int d0_bignum_isprime(d0_bignum_t *r, int param);
+WARN_UNUSED_RESULT d0_bignum_t *d0_bignum_gcd(d0_bignum_t *r, d0_bignum_t *s, d0_bignum_t *t, const d0_bignum_t *a, const d0_bignum_t *b);
+
+WARN_UNUSED_RESULT char *d0_bignum_tostring(const d0_bignum_t *x, unsigned int base); // allocates!
diff --git a/d0_blind_id.c b/d0_blind_id.c
new file mode 100644 (file)
index 0000000..1059ffe
--- /dev/null
@@ -0,0 +1,655 @@
+#include "d0_blind_id.h"
+
+#include <stdio.h>
+#include <string.h>
+#include "d0_bignum.h"
+#include "sha1.h"
+
+// for zero knowledge, we need multiple instances of schnorr ID scheme... should normally be sequential
+// parallel schnorr ID is not provably zero knowledge :(
+//   (evil verifier can know all questions in advance, so sequential is disadvantage for him)
+// we'll just live with a 1:1048576 chance of cheating, and support reauthenticating
+
+#define SCHNORR_BITS 20
+// probability of cheat: 2^(-bits+1)
+
+#define SCHNORR_HASHSIZE 3
+// cannot be >= SHA_DIGEST_LENGTH
+// *8 must be >= SCHNORR_BITS
+
+#define MSGSIZE 640 // ought to be enough for anyone
+
+struct d0_blind_id_s
+{
+       // signing (Xonotic pub and priv key)
+       d0_bignum_t *rsa_n, *rsa_e, *rsa_d;
+
+       // public data (Schnorr ID)
+       d0_bignum_t *schnorr_G;
+
+       // private data (player ID private key)
+       d0_bignum_t *schnorr_s;
+
+       // public data (player ID public key, this is what the server gets to know)
+       d0_bignum_t *schnorr_4_to_s;
+       d0_bignum_t *schnorr_4_to_s_signature;
+
+       // temp data
+       d0_bignum_t *rn; // random number blind signature
+       d0_bignum_t *r; // random number for schnorr ID
+       char xnbh[SCHNORR_HASHSIZE]; // init hash
+       d0_bignum_t *e; // challenge
+       char msg[MSGSIZE]; // message
+       size_t msglen; // message length
+};
+
+#define CHECK(x) do { if(!(x)) goto fail; } while(0)
+#define CHECK_ASSIGN(var, value) do { d0_bignum_t *val; val = value; if(!val) goto fail; var = val; } while(0)
+
+static d0_bignum_t *zero, *one, *four, *temp0, *temp1, *temp2, *temp3, *temp4;
+
+void d0_blind_id_INITIALIZE()
+{
+       d0_bignum_INITIALIZE();
+       CHECK_ASSIGN(zero, d0_bignum_int(zero, 0));
+       CHECK_ASSIGN(one, d0_bignum_int(one, 1));
+       CHECK_ASSIGN(four, d0_bignum_int(four, 4));
+       CHECK_ASSIGN(temp0, d0_bignum_int(temp0, 0));
+       CHECK_ASSIGN(temp1, d0_bignum_int(temp1, 0));
+       CHECK_ASSIGN(temp2, d0_bignum_int(temp2, 0));
+       CHECK_ASSIGN(temp3, d0_bignum_int(temp3, 0));
+       CHECK_ASSIGN(temp4, d0_bignum_int(temp4, 0));
+fail:
+       ;
+}
+
+void d0_blind_id_SHUTDOWN()
+{
+       d0_bignum_free(zero);
+       d0_bignum_free(one);
+       d0_bignum_free(four);
+       d0_bignum_free(temp0);
+       d0_bignum_free(temp1);
+       d0_bignum_free(temp2);
+       d0_bignum_free(temp3);
+       d0_bignum_free(temp4);
+       d0_bignum_SHUTDOWN();
+}
+
+// (G-1)/2
+d0_bignum_t *d0_dl_get_order(d0_bignum_t *o, const d0_bignum_t *G)
+{
+       CHECK_ASSIGN(o, d0_bignum_sub(o, G, one));
+       CHECK(d0_bignum_shl(o, o, -1)); // order o = (G-1)/2
+       return o;
+fail:
+       return NULL;
+}
+// 2o+1
+d0_bignum_t *d0_dl_get_from_order(d0_bignum_t *G, const d0_bignum_t *o)
+{
+       CHECK_ASSIGN(G, d0_bignum_shl(G, o, 1));
+       CHECK(d0_bignum_add(G, G, one));
+       return G;
+fail:
+       return NULL;
+}
+
+BOOL d0_dl_generate_key(size_t size, d0_bignum_t *G)
+{
+       // using: temp0
+       if(size < 16)
+               size = 16;
+       for(;;)
+       {
+               CHECK(d0_bignum_rand_bit_exact(temp0, size-1));
+               if(d0_bignum_isprime(temp0, 0) == 0)
+                       continue;
+               CHECK(d0_dl_get_from_order(G, temp0));
+               if(d0_bignum_isprime(G, 10) == 0)
+                       continue;
+               if(d0_bignum_isprime(temp0, 10) == 0) // finish the previous test
+                       continue;
+               break;
+       }
+       return 1;
+fail:
+       return 0;
+}
+
+BOOL d0_rsa_generate_key(size_t size, const d0_bignum_t *e, d0_bignum_t *d, d0_bignum_t *n)
+{
+       // uses temp0 to temp4
+       int fail = 0;
+       int gcdfail = 0;
+       if(size < 16)
+               size = 16;
+       int pb = (size + 1)/2;
+       int qb = size - pb;
+        for (;;)
+       {
+               CHECK(d0_bignum_rand_bit_exact(temp0, pb));
+               if(d0_bignum_isprime(temp0, 10) == 0)
+                       continue;
+               CHECK(d0_bignum_sub(temp2, temp0, one));
+               CHECK(d0_bignum_gcd(temp4, NULL, NULL, temp2, e));
+               if(!d0_bignum_cmp(temp4, one))
+                       break;
+               if(++gcdfail == 3)
+                       return 0;
+               ++gcdfail;
+       }
+       gcdfail = 0;
+        for (;;)
+       {
+               CHECK(d0_bignum_rand_bit_exact(temp1, qb));
+               if(!d0_bignum_cmp(temp1, temp0))
+               {
+                       if(++fail == 3)
+                               return 0;
+               }
+               fail = 0;
+               if(d0_bignum_isprime(temp1, 10) == 0)
+                       continue;
+               CHECK(d0_bignum_sub(temp3, temp1, one));
+               CHECK(d0_bignum_gcd(temp4, NULL, NULL, temp3, e));
+               if(!d0_bignum_cmp(temp4, one))
+                       break;
+               if(++gcdfail == 3)
+                       return 0;
+               ++gcdfail;
+       }
+
+       // n = temp0*temp1
+       CHECK(d0_bignum_mul(n, temp0, temp1));
+               
+       // d = e^-1 mod (temp0-1)(temp1-1)
+       CHECK(d0_bignum_mul(temp0, temp2, temp3));
+       CHECK(d0_bignum_mod_inv(d, e, temp0));
+       return 1;
+fail:
+       return 0;
+}
+
+void d0_blind_id_clear(d0_blind_id_t *ctx)
+{
+       if(ctx->rsa_n) d0_bignum_free(ctx->rsa_n);
+       if(ctx->rsa_e) d0_bignum_free(ctx->rsa_e);
+       if(ctx->rsa_d) d0_bignum_free(ctx->rsa_d);
+       if(ctx->schnorr_G) d0_bignum_free(ctx->schnorr_G);
+       if(ctx->schnorr_s) d0_bignum_free(ctx->schnorr_s);
+       if(ctx->schnorr_4_to_s) d0_bignum_free(ctx->schnorr_4_to_s);
+       if(ctx->schnorr_4_to_s_signature) d0_bignum_free(ctx->schnorr_4_to_s_signature);
+       if(ctx->rn) d0_bignum_free(ctx->rn);
+       if(ctx->r) d0_bignum_free(ctx->r);
+       if(ctx->e) d0_bignum_free(ctx->e);
+       memset(ctx, 0, sizeof(*ctx));
+}
+
+void d0_blind_id_copy(d0_blind_id_t *ctx, const d0_blind_id_t *src)
+{
+       d0_blind_id_clear(ctx);
+       if(src->rsa_n) ctx->rsa_n = d0_bignum_mov(NULL, src->rsa_n);
+       if(src->rsa_e) ctx->rsa_e = d0_bignum_mov(NULL, src->rsa_e);
+       if(src->rsa_d) ctx->rsa_d = d0_bignum_mov(NULL, src->rsa_d);
+       if(src->schnorr_G) ctx->schnorr_G = d0_bignum_mov(NULL, src->schnorr_G);
+       if(src->schnorr_s) ctx->schnorr_s = d0_bignum_mov(NULL, src->schnorr_s);
+       if(src->schnorr_4_to_s) ctx->schnorr_4_to_s = d0_bignum_mov(NULL, ctx->schnorr_G);
+       if(src->schnorr_4_to_s_signature) ctx->schnorr_4_to_s_signature = d0_bignum_mov(NULL, src->schnorr_4_to_s_signature);
+       if(src->rn) ctx->rn = d0_bignum_mov(NULL, src->rn);
+       if(src->r) ctx->r = d0_bignum_mov(NULL, src->r);
+       if(src->e) ctx->e = d0_bignum_mov(NULL, src->e);
+       // TODO xnbh, msg, msglen?
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_keys(d0_blind_id_t *ctx, int k)
+{
+       d0_blind_id_clear(ctx);
+       CHECK_ASSIGN(ctx->schnorr_G, d0_bignum_new());
+       CHECK(d0_dl_generate_key(k, ctx->schnorr_G));
+       CHECK_ASSIGN(ctx->rsa_e, d0_bignum_int(NULL, 65537));
+       CHECK_ASSIGN(ctx->rsa_d, d0_bignum_new());
+       CHECK_ASSIGN(ctx->rsa_n, d0_bignum_new());
+       CHECK(d0_rsa_generate_key(k+1, ctx->rsa_e, ctx->rsa_d, ctx->rsa_n)); // must fit G for sure
+       return 1;
+fail:
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_private_keys(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
+{
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+       d0_blind_id_clear(ctx);
+       CHECK_ASSIGN(ctx->schnorr_G, d0_iobuf_read_bignum(in, NULL));
+       CHECK_ASSIGN(ctx->rsa_n, d0_iobuf_read_bignum(in, NULL));
+       CHECK_ASSIGN(ctx->rsa_e, d0_iobuf_read_bignum(in, NULL));
+       CHECK_ASSIGN(ctx->rsa_d, d0_iobuf_read_bignum(in, NULL));
+       return d0_iobuf_close(in, NULL);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_public_keys(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
+{
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+       d0_blind_id_clear(ctx);
+       CHECK_ASSIGN(ctx->schnorr_G, d0_iobuf_read_bignum(in, NULL));
+       CHECK_ASSIGN(ctx->rsa_n, d0_iobuf_read_bignum(in, NULL));
+       CHECK_ASSIGN(ctx->rsa_e, d0_iobuf_read_bignum(in, NULL));
+       return d0_iobuf_close(in, NULL);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       return 0;
+}
+
+#define USING(x) if(!(ctx->x)) return 0
+#define WRITING(x,f) if(ctx->x) { f(ctx->x); ctx->x = NULL; }
+#define REPLACING(x)
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_private_keys(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
+{
+       USING(rsa_n); USING(rsa_e); USING(rsa_d);
+
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_G));
+       CHECK(d0_iobuf_write_bignum(out, ctx->rsa_n));
+       CHECK(d0_iobuf_write_bignum(out, ctx->rsa_e));
+       CHECK(d0_iobuf_write_bignum(out, ctx->rsa_d));
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_public_keys(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
+{
+       USING(rsa_n); USING(rsa_e); USING(rsa_d);
+
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_G));
+       CHECK(d0_iobuf_write_bignum(out, ctx->rsa_n));
+       CHECK(d0_iobuf_write_bignum(out, ctx->rsa_e));
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       if(!d0_iobuf_close(out, outbuflen))
+               return 0;
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_id_start(d0_blind_id_t *ctx)
+{
+       // temps: temp0 order
+       USING(schnorr_G);
+       REPLACING(schnorr_s); REPLACING(schnorr_4_to_s);
+
+       CHECK(d0_dl_get_order(temp0, ctx->schnorr_G));
+       CHECK_ASSIGN(ctx->schnorr_s, d0_bignum_rand_range(ctx->schnorr_s, zero, temp0));
+       CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_bignum_mod_pow(ctx->schnorr_4_to_s, four, ctx->schnorr_s, ctx->schnorr_G));
+       return 1;
+
+fail:
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_id_request(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
+{
+       // temps: temp0 temp1
+       USING(rsa_n); USING(rsa_e); USING(schnorr_4_to_s);
+       REPLACING(rn);
+
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       CHECK_ASSIGN(ctx->rn, d0_bignum_rand_bit_atmost(ctx->rn, d0_bignum_size(ctx->rsa_n)));
+       CHECK(d0_bignum_mod_pow(temp0, ctx->rn, ctx->rsa_e, ctx->rsa_n));
+       CHECK(d0_bignum_mod_mul(temp1, ctx->schnorr_4_to_s, temp0, ctx->rsa_n));
+       CHECK(d0_iobuf_write_bignum(out, temp1));
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_answer_private_id_request(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen)
+{
+       // temps: temp0 temp1
+       USING(rsa_d); USING(rsa_n);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       CHECK(d0_iobuf_read_bignum(in, temp0));
+       CHECK(d0_bignum_mod_pow(temp1, temp0, ctx->rsa_d, ctx->rsa_n));
+       CHECK(d0_iobuf_write_bignum(out, temp1));
+
+       d0_iobuf_close(in, NULL);
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_finish_private_id_request(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
+{
+       // temps: temp0 temp1
+       USING(rn); USING(rsa_n);
+       REPLACING(schnorr_4_to_s_signature);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+
+       CHECK(d0_iobuf_read_bignum(in, temp0));
+       CHECK(d0_bignum_mod_inv(temp1, ctx->rn, ctx->rsa_n));
+       CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_bignum_mod_mul(ctx->schnorr_4_to_s_signature, temp0, temp1, ctx->rsa_n));
+
+       return d0_iobuf_close(in, NULL);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_private_id(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
+{
+       REPLACING(schnorr_s); REPLACING(schnorr_4_to_s); REPLACING(schnorr_4_to_s_signature);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+
+       CHECK_ASSIGN(ctx->schnorr_s, d0_iobuf_read_bignum(in, ctx->schnorr_s));
+       CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s));
+       CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s_signature));
+
+       return d0_iobuf_close(in, NULL);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_public_id(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
+{
+       REPLACING(schnorr_4_to_s); REPLACING(schnorr_4_to_s_signature);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+
+       CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s));
+       CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s_signature));
+
+       return d0_iobuf_close(in, NULL);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_private_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
+{
+       USING(schnorr_s); USING(schnorr_4_to_s); USING(schnorr_4_to_s_signature);
+
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_s));
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s));
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s_signature));
+
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_public_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
+{
+       USING(schnorr_4_to_s); USING(schnorr_4_to_s_signature);
+
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s));
+       CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s_signature));
+
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_start(d0_blind_id_t *ctx, int is_first, char *msg, size_t msglen, char *outbuf, size_t *outbuflen)
+// start =
+//   first run: send 4^s, 4^s signature
+//   1. get random r, send HASH(4^r)
+{
+       if(is_first)
+       {
+               USING(schnorr_4_to_s); USING(schnorr_4_to_s_signature);
+       }
+       USING(schnorr_G);
+       REPLACING(r);
+
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       if(is_first)
+       {
+               // send ID
+               CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s));
+               CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s_signature));
+       }
+
+       // start schnorr ID scheme
+       // generate random number r; x = g^r; send hash of x, remember r, forget x
+       CHECK(d0_dl_get_order(temp0, ctx->schnorr_G));
+       CHECK_ASSIGN(ctx->r, d0_bignum_rand_range(ctx->r, zero, temp0));
+       CHECK(d0_bignum_mod_pow(temp0, four, ctx->r, ctx->schnorr_G));
+
+       // hash it, hash it, everybody hash it
+       unsigned char convbuf[1024];
+       d0_iobuf_t *conv = d0_iobuf_open_write(convbuf, sizeof(convbuf));
+       size_t sz;
+       CHECK(d0_iobuf_write_bignum(conv, temp0));
+       CHECK(d0_iobuf_write_packet(conv, msg, msglen));
+       CHECK(d0_iobuf_write_bignum(conv, temp0));
+       d0_iobuf_close(conv, &sz);
+       conv = NULL;
+       CHECK(d0_iobuf_write_raw(out, sha(convbuf, sz), SCHNORR_HASHSIZE) == SCHNORR_HASHSIZE);
+       CHECK(d0_iobuf_write_packet(out, msg, msglen));
+
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_challenge(d0_blind_id_t *ctx, int is_first, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen)
+//   first run: get 4^s, 4^s signature
+//   1. check sig
+//   2. save HASH(4^r)
+//   3. send challenge e of SCHNORR_BITS
+{
+       if(is_first)
+       {
+               REPLACING(schnorr_4_to_s); REPLACING(k); REPLACING(schnorr_4_to_s_signature);
+               USING(schnorr_G); USING(rsa_n);
+       }
+       else
+       {
+               USING(schnorr_4_to_s_signature); USING(schnorr_4_to_s);
+       }
+       USING(rsa_e); USING(rsa_n);
+       REPLACING(e); REPLACING(msg); REPLACING(msglen);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       if(is_first)
+       {
+               CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_iobuf_read_bignum(in, NULL));
+               CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s, zero) >= 0);
+               CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s, ctx->schnorr_G) < 0);
+               CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s_signature));
+               CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s_signature, zero) >= 0);
+               CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s_signature, ctx->rsa_n) < 0);
+       }
+
+       // check signature of key (t = k^d, so, t^e = k)
+       CHECK(d0_bignum_mod_pow(temp0, ctx->schnorr_4_to_s_signature, ctx->rsa_e, ctx->rsa_n));
+       if(d0_bignum_cmp(temp0, ctx->schnorr_4_to_s))
+       {
+               // FAIL (not signed by Xonotic)
+               goto fail;
+               // TODO: accept the key anyway, but mark as failed signature!
+       }
+
+       CHECK(d0_iobuf_read_raw(in, ctx->xnbh, SCHNORR_HASHSIZE));
+       ctx->msglen = MSGSIZE;
+       CHECK(d0_iobuf_read_packet(in, ctx->msg, &ctx->msglen));
+
+       // send challenge
+       CHECK_ASSIGN(ctx->e, d0_bignum_rand_bit_atmost(ctx->e, SCHNORR_BITS));
+
+       CHECK(d0_iobuf_write_bignum(out, ctx->e));
+
+       d0_iobuf_close(in, NULL);
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_response(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen)
+//   1. read challenge e of SCHNORR_BITS
+//   2. reply with r + s * e mod order
+{
+       // temps: 0 order, 1 prod, 2 y, 3 e
+       USING(schnorr_G); USING(schnorr_s); USING(r);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+
+       CHECK(d0_iobuf_read_bignum(in, temp3));
+       // TODO check if >= 2^SCHNORR_BITS or < 0, if yes, then fail (needed for zero knowledge)
+       CHECK(d0_bignum_cmp(temp3, zero) >= 0);
+       CHECK(d0_bignum_size(temp3) <= SCHNORR_BITS);
+
+       // send response for schnorr ID scheme
+       // i.e. r + ctx->schnorr_s * temp3
+       CHECK(d0_dl_get_order(temp0, ctx->schnorr_G));
+       CHECK(d0_bignum_mod_mul(temp1, ctx->schnorr_s, temp3, temp0));
+       CHECK(d0_bignum_mod_add(temp2, temp1, ctx->r, temp0));
+       CHECK(d0_iobuf_write_bignum(out, temp2));
+
+       d0_iobuf_close(in, NULL);
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       d0_iobuf_close(in, NULL);
+       d0_iobuf_close(out, outbuflen);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_verify(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *msg, ssize_t *msglen)
+//   1. read y = r + s * e mod order
+//   2. verify: g^y (g^s)^-e = g^(r+s*e-s*e) = g^r
+//      (check using H(g^r) which we know)
+{
+       // temps: 0 y 1 order
+       USING(e); USING(schnorr_G);
+
+       d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
+
+       *msglen = -1;
+       CHECK(d0_dl_get_order(temp1, ctx->schnorr_G));
+       CHECK(d0_iobuf_read_bignum(in, temp0));
+       CHECK(d0_bignum_cmp(temp0, zero) >= 0);
+       CHECK(d0_bignum_cmp(temp0, temp1) < 0);
+
+       // verify schnorr ID scheme
+       // we need 4^temp0 (g^s)^-e
+       CHECK(d0_bignum_neg(temp1, ctx->e));
+       CHECK(d0_bignum_mod_pow(temp2, ctx->schnorr_4_to_s, temp1, ctx->schnorr_G));
+       CHECK(d0_bignum_mod_pow(temp1, four, temp0, ctx->schnorr_G));
+       CHECK(d0_bignum_mod_mul(temp3, temp1, temp2, ctx->schnorr_G));
+       // hash must be equal to xnbh
+
+       // hash it, hash it, everybody hash it
+       unsigned char convbuf[1024];
+       d0_iobuf_t *conv = d0_iobuf_open_write(convbuf, sizeof(convbuf));
+       size_t sz;
+       CHECK(d0_iobuf_write_bignum(conv, temp3));
+       CHECK(d0_iobuf_write_packet(conv, ctx->msg, ctx->msglen));
+       CHECK(d0_iobuf_write_bignum(conv, temp3));
+       d0_iobuf_close(conv, &sz);
+       conv = NULL;
+       if(memcmp(sha(convbuf, sz), ctx->xnbh, SCHNORR_HASHSIZE))
+       {
+               // FAIL (not owned by player)
+               goto fail;
+       }
+
+       if(ctx->msglen <= (size_t) *msglen)
+               memcpy(msg, ctx->msg, ctx->msglen);
+       else
+               memcpy(msg, ctx->msg, *msglen);
+       *msglen = ctx->msglen;
+
+       d0_iobuf_close(in, NULL);
+       return 1;
+
+fail:
+       d0_iobuf_close(in, NULL);
+       return 0;
+}
+
+WARN_UNUSED_RESULT BOOL d0_blind_id_fingerprint64_public_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
+{
+       USING(schnorr_4_to_s);
+
+       static unsigned char convbuf[1024];
+       d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
+       d0_iobuf_t *conv = d0_iobuf_open_write(convbuf, sizeof(convbuf));
+
+       size_t n, sz;
+
+       CHECK(d0_iobuf_write_bignum(conv, ctx->schnorr_4_to_s));
+       CHECK(d0_iobuf_close(conv, &sz));
+       conv = NULL;
+
+       n = (*outbuflen / 4) * 3;
+       if(n > SHA_DIGESTSIZE)
+               n = SHA_DIGESTSIZE;
+       if(d0_iobuf_write_raw(out, sha(convbuf, sz), n) != n)
+               goto fail;
+       if(!d0_iobuf_conv_base64_out(out))
+               goto fail;
+
+       return d0_iobuf_close(out, outbuflen);
+
+fail:
+       if(conv)
+               if(!d0_iobuf_close(conv, &sz)) { }
+       if(!d0_iobuf_close(out, outbuflen))
+               return 0;
+       return 0;
+}
+
+d0_blind_id_t *d0_blind_id_new()
+{
+       d0_blind_id_t *b = d0_malloc(sizeof(d0_blind_id_t));
+       memset(b, 0, sizeof(*b));
+       return b;
+}
+
+void d0_blind_id_free(d0_blind_id_t *a)
+{
+       d0_blind_id_clear(a);
+       d0_free(a);
+}
diff --git a/d0_blind_id.h b/d0_blind_id.h
new file mode 100644 (file)
index 0000000..849285f
--- /dev/null
@@ -0,0 +1,29 @@
+#include "d0.h"
+
+typedef struct d0_blind_id_s d0_blind_id_t;
+
+WARN_UNUSED_RESULT d0_blind_id_t *d0_blind_id_new();
+void d0_blind_id_free(d0_blind_id_t *a);
+void d0_blind_id_clear(d0_blind_id_t *ctx);
+void d0_blind_id_copy(d0_blind_id_t *ctx, const d0_blind_id_t *src);
+WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_keys(d0_blind_id_t *ctx, int k);
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_private_keys(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_public_keys(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_private_keys(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_public_keys(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_id_start(d0_blind_id_t *ctx);
+WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_id_request(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_answer_private_id_request(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_finish_private_id_request(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_private_id(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_read_public_id(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_private_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_write_public_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_start(d0_blind_id_t *ctx, int is_first, char *message, size_t msglen, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_challenge(d0_blind_id_t *ctx, int is_first, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_response(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_verify(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *msg, ssize_t *msglen);
+WARN_UNUSED_RESULT BOOL d0_blind_id_fingerprint64_public_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
+
+void d0_blind_id_INITIALIZE();
+void d0_blind_id_SHUTDOWN();
diff --git a/d0_iobuf.c b/d0_iobuf.c
new file mode 100644 (file)
index 0000000..7c676f4
--- /dev/null
@@ -0,0 +1,148 @@
+#include "d0_iobuf.h"
+
+#include <string.h>
+
+struct d0_iobuf_s
+{
+       const unsigned char *inbuf;
+       unsigned char *outbuf;
+       size_t inpos, outpos, inbuflen, outbuflen;
+       BOOL ok;
+};
+
+d0_iobuf_t *d0_iobuf_open_read(const void *buf, size_t len)
+{
+       d0_iobuf_t *b = d0_malloc(sizeof(d0_iobuf_t));
+       b->inbuf = (const unsigned char *) buf;
+       b->outbuf = NULL;
+       b->inpos = b->outpos = 0;
+       b->inbuflen = len;
+       b->outbuflen = 0;
+       b->ok = 1;
+       return b;
+}
+
+d0_iobuf_t *d0_iobuf_open_write(void *buf, size_t len)
+{
+       d0_iobuf_t *b = d0_malloc(sizeof(d0_iobuf_t));
+       b->inbuf = (const unsigned char *) buf;
+       b->outbuf = (unsigned char *) buf;
+       b->inpos = b->outpos = 0;
+       b->inbuflen = len;
+       b->outbuflen = len;
+       b->ok = 1;
+       return b;
+}
+
+BOOL d0_iobuf_close(d0_iobuf_t *buf, size_t *len)
+{
+       BOOL r = buf->ok;
+       if(len)
+               *len = buf->outpos;
+       d0_free(buf);
+       return r;
+}
+
+size_t d0_iobuf_write_raw(d0_iobuf_t *buf, const void *s, size_t n)
+{
+       size_t nreal = n;
+       if(nreal > buf->outbuflen - buf->outpos)
+       {
+               buf->ok = 0;
+               nreal = buf->outbuflen - buf->outpos;
+       }
+       memcpy(buf->outbuf + buf->outpos, s, nreal);
+       buf->outpos += nreal;
+       return nreal;
+}
+
+size_t d0_iobuf_read_raw(d0_iobuf_t *buf, void *s, size_t n)
+{
+       size_t nreal = n;
+       if(nreal > buf->inbuflen - buf->inpos)
+       {
+               buf->ok = 0;
+               nreal = buf->inbuflen - buf->inpos;
+       }
+       memcpy(s, buf->inbuf + buf->inpos, nreal);
+       buf->inpos += nreal;
+       return nreal;
+}
+
+BOOL d0_iobuf_write_packet(d0_iobuf_t *buf, const void *s, size_t n)
+{
+       unsigned char c;
+       size_t nn = n;
+       while(nn)
+       {
+               c = nn & 255;
+               nn >>= 8;
+               if(d0_iobuf_write_raw(buf, &c, 1) != 1)
+                       return 0;
+       }
+       c = 0;
+       if(d0_iobuf_write_raw(buf, &c, 1) != 1)
+               return 0;
+       if(d0_iobuf_write_raw(buf, s, n) != n)
+               return 0;
+       return 1;
+}
+
+BOOL d0_iobuf_read_packet(d0_iobuf_t *buf, void *s, size_t *np)
+{
+       unsigned char c;
+       size_t n = 0;
+       size_t nn = 1;
+       do
+       {
+               if(d0_iobuf_read_raw(buf, &c, 1) != 1)
+                       return 0;
+               n |= nn * c;
+               nn <<= 8;
+       }
+       while(c);
+       if(n > *np)
+               return 0;
+       if(d0_iobuf_read_raw(buf, s, n) != n)
+               return 0;
+       *np = n;
+       return 1;
+}
+
+BOOL d0_iobuf_conv_base64_in(d0_iobuf_t *buf)
+{
+       // compand the in-buffer
+       return 0;
+}
+
+static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
+{
+       unsigned char i0 = (bytes > 0) ? in[0] : 0;
+       unsigned char i1 = (bytes > 1) ? in[1] : 0;
+       unsigned char i2 = (bytes > 2) ? in[2] : 0;
+       unsigned char o0 = base64[i0 >> 2];
+       unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
+       unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
+       unsigned char o3 = base64[i2 & 077];
+       out[0] = (bytes > 0) ? o0 : '?';
+       out[1] = (bytes > 0) ? o1 : '?';
+       out[2] = (bytes > 1) ? o2 : '=';
+       out[3] = (bytes > 2) ? o3 : '=';
+}
+
+BOOL d0_iobuf_conv_base64_out(d0_iobuf_t *buf)
+{
+       size_t blocks, i;
+       // expand the out-buffer
+       blocks = ((buf->outpos + 2) / 3);
+       if(blocks*4 > buf->outbuflen)
+               return 0;
+       for(i = blocks; i > 0; )
+       {
+               --i;
+               base64_3to4(buf->outbuf + 3*i, buf->outbuf + 4*i, buf->outpos - 3*i);
+       }
+       buf->outpos = blocks * 4;
+       return 1;
+}
diff --git a/d0_iobuf.h b/d0_iobuf.h
new file mode 100644 (file)
index 0000000..396215d
--- /dev/null
@@ -0,0 +1,13 @@
+#include "d0.h"
+
+typedef struct d0_iobuf_s d0_iobuf_t;
+
+WARN_UNUSED_RESULT d0_iobuf_t *d0_iobuf_open_read(const void *buf, size_t len); // note: can read AND write!
+WARN_UNUSED_RESULT d0_iobuf_t *d0_iobuf_open_write(void *buf, size_t len); // note: can read AND write!
+WARN_UNUSED_RESULT BOOL d0_iobuf_conv_base64_in(d0_iobuf_t *buf);
+WARN_UNUSED_RESULT BOOL d0_iobuf_conv_base64_out(d0_iobuf_t *buf);
+BOOL d0_iobuf_close(d0_iobuf_t *buf, size_t *len); // don't warn
+WARN_UNUSED_RESULT size_t d0_iobuf_write_raw(d0_iobuf_t *buf, const void *s, size_t n);
+WARN_UNUSED_RESULT size_t d0_iobuf_read_raw(d0_iobuf_t *buf, void *s, size_t n);
+WARN_UNUSED_RESULT BOOL d0_iobuf_write_packet(d0_iobuf_t *buf, const void *s, size_t n);
+WARN_UNUSED_RESULT BOOL d0_iobuf_read_packet(d0_iobuf_t *buf, void *s, size_t *n);
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..88bf520
--- /dev/null
+++ b/main.c
@@ -0,0 +1,103 @@
+#include "d0_blind_id.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+void bench(double *b)
+{
+       static struct timespec thistime, lasttime;
+       static double x = 0;
+       static double *lastclock = &x;
+       lasttime = thistime;
+       clock_gettime(CLOCK_MONOTONIC, &thistime);
+       *lastclock += (thistime.tv_sec - lasttime.tv_sec) + 0.000000001 * (thistime.tv_nsec - lasttime.tv_nsec);
+       lastclock = b;
+}
+
+#include <sys/signal.h>
+volatile BOOL quit = 0;
+void mysignal(int signo)
+{
+       (void) signo;
+       quit = 1;
+}
+
+#include <err.h>
+int main(int argc, char **argv)
+{
+       char buf[65536]; size_t bufsize;
+       char buf2[65536]; size_t buf2size; ssize_t buf2ssize;
+       d0_blind_id_t *ctx_self, *ctx_other;
+
+       d0_blind_id_INITIALIZE();
+       ctx_self = d0_blind_id_new();
+       ctx_other = d0_blind_id_new();
+
+       if(!d0_blind_id_generate_private_keys(ctx_self, 1024))
+               errx(1, "keygen fail");
+       bufsize = sizeof(buf); if(!d0_blind_id_write_public_keys(ctx_self, buf, &bufsize))
+               errx(2, "writepub fail");
+       if(!d0_blind_id_read_public_keys(ctx_other, buf, bufsize))
+               errx(3, "readpub fail");
+
+       signal(SIGINT, mysignal);
+
+       int n = 0;
+       double bench_gen = 0, bench_fp = 0, bench_stop = 0;
+       do
+       {
+               bench(&bench_gen);
+               bufsize = sizeof(buf); if(!d0_blind_id_generate_private_id_start(ctx_other))
+                       errx(4, "genid fail");
+               bench(&bench_fp);
+               buf2size = sizeof(buf2) - 1; if(!d0_blind_id_fingerprint64_public_id(ctx_other, buf2, &buf2size))
+                       errx(4, "fp64 fail");
+               bench(&bench_stop);
+               if(n % 1024 == 0)
+                       printf("gen=%f fp=%f\n", n/bench_gen, n/bench_fp);
+               ++n;
+       }
+       while(!(quit || argc != 2 || (buf2size > strlen(argv[1]) && !memcmp(buf2, argv[1], strlen(argv[1])))));
+
+       buf2[buf2size] = 0;
+       printf("Generated key has ID: %s\n", buf2);
+
+       bufsize = sizeof(buf); if(!d0_blind_id_generate_private_id_request(ctx_other, buf, &bufsize))
+               errx(4, "genreq fail");
+       buf2size = sizeof(buf2); if(!d0_blind_id_answer_private_id_request(ctx_self, buf, bufsize, buf2, &buf2size))
+               errx(5, "ansreq fail");
+       if(!d0_blind_id_finish_private_id_request(ctx_other, buf2, buf2size))
+               errx(6, "finishreq fail");
+
+       bufsize = sizeof(buf); if(!d0_blind_id_write_public_id(ctx_other, buf, &bufsize))
+               errx(7, "writepub2 fail");
+       if(!d0_blind_id_read_public_id(ctx_self, buf, bufsize))
+               errx(8, "readpub2 fail");
+
+       n = 0;
+       double bench_auth = 0, bench_chall = 0, bench_resp = 0, bench_verify = 0;
+       while(!quit)
+       {
+               bench(&bench_auth);
+               bufsize = sizeof(buf); if(!d0_blind_id_authenticate_with_private_id_start(ctx_other, 1, "hello world", 11, buf, &bufsize))
+                       errx(9, "start fail");
+               bench(&bench_chall);
+               buf2size = sizeof(buf2); if(!d0_blind_id_authenticate_with_private_id_challenge(ctx_self, 1, buf, bufsize, buf2, &buf2size))
+                       errx(10, "challenge fail");
+               bench(&bench_resp);
+               bufsize = sizeof(buf); if(!d0_blind_id_authenticate_with_private_id_response(ctx_other, buf2, buf2size, buf, &bufsize))
+                       errx(11, "response fail");
+               bench(&bench_verify);
+               buf2ssize = sizeof(buf2); if(!d0_blind_id_authenticate_with_private_id_verify(ctx_self, buf, bufsize, buf2, &buf2ssize))
+                       errx(12, "verify fail");
+               if(buf2ssize != 11 || memcmp(buf2, "hello world", 11))
+                       errx(13, "hello fail");
+               bench(&bench_stop);
+               ++n;
+               if(n % 1024 == 0)
+                       printf("auth=%f chall=%f resp=%f verify=%f\n", n/bench_auth, n/bench_chall, n/bench_resp, n/bench_verify);
+       }
+
+       return 0;
+}
diff --git a/sha1.c b/sha1.c
new file mode 100644 (file)
index 0000000..dfeda6f
--- /dev/null
+++ b/sha1.c
@@ -0,0 +1,377 @@
+/* sha.c - Implementation of the Secure Hash Algorithm
+ *
+ * Copyright (C) 1995, A.M. Kuchling
+ *
+ * Distribute and use freely; there are no restrictions on further 
+ * dissemination and usage except those imposed by the laws of your 
+ * country of residence.
+ *
+ * Adapted to pike and some cleanup by Niels Möller.
+ */
+
+/* $Id: sha1.c,v 1.6 2006/01/08 09:08:29 imipak Exp $ */
+
+/* SHA: NIST's Secure Hash Algorithm */
+
+/* Based on SHA code originally posted to sci.crypt by Peter Gutmann
+   in message <30ajo5$oe8@ccu2.auckland.ac.nz>.
+   Modified to test for endianness on creation of SHA objects by AMK.
+   Also, the original specification of SHA was found to have a weakness
+   by NSA/NIST.  This code implements the fixed version of SHA.
+*/
+
+/* Here's the first paragraph of Peter Gutmann's posting:
+   
+The following is my SHA (FIPS 180) code updated to allow use of the "fixed"
+SHA, thanks to Jim Gillogly and an anonymous contributor for the information on
+what's changed in the new version.  The fix is a simple change which involves
+adding a single rotate in the initial expansion function.  It is unknown
+whether this is an optimal solution to the problem which was discovered in the
+SHA or whether it's simply a bandaid which fixes the problem with a minimum of
+effort (for example the reengineering of a great many Capstone chips).
+*/
+
+#include <string.h>
+#include "sha1.h"
+
+void sha_copy(struct sha_ctx *dest, struct sha_ctx *src)
+{
+       unsigned int i;
+
+       dest->count_l=src->count_l;
+       dest->count_h=src->count_h;
+       for(i=0; i<SHA_DIGESTLEN; i++)
+       {
+               dest->digest[i]=src->digest[i];
+       }
+       for(i=0; i < src->index; i++)
+       {
+               dest->block[i] = src->block[i];
+       }
+       dest->index = src->index;
+}
+
+
+/* The SHA f()-functions.  The f1 and f3 functions can be optimized to
+   save one boolean operation each - thanks to Rich Schroeppel,
+   rcs@cs.arizona.edu for discovering this */
+
+#define f1(x,y,z)   ( z ^ ( x & ( y ^ z ) ) )           /* Rounds  0-19 */
+#define f2(x,y,z)   ( x ^ y ^ z )                       /* Rounds 20-39 */
+#define f3(x,y,z)   ( ( x & y ) | ( z & ( x | y ) ) )   /* Rounds 40-59 */
+#define f4(x,y,z)   ( x ^ y ^ z )                       /* Rounds 60-79 */
+
+/* The SHA Mysterious Constants */
+
+#define K1  0x5A827999L                                 /* Rounds  0-19 */
+#define K2  0x6ED9EBA1L                                 /* Rounds 20-39 */
+#define K3  0x8F1BBCDCL                                 /* Rounds 40-59 */
+#define K4  0xCA62C1D6L                                 /* Rounds 60-79 */
+
+/* SHA initial values */
+
+#define h0init  0x67452301L
+#define h1init  0xEFCDAB89L
+#define h2init  0x98BADCFEL
+#define h3init  0x10325476L
+#define h4init  0xC3D2E1F0L
+
+/* 32-bit rotate left - kludged with shifts */
+
+#define ROTL(n,X)  ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) )
+
+/* The initial expanding function.  The hash function is defined over an
+   80-word expanded input array W, where the first 16 are copies of the input
+   data, and the remaining 64 are defined by
+
+        W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]
+
+   This implementation generates these values on the fly in a circular
+   buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
+   optimization.
+
+   The updated SHA changes the expanding function by adding a rotate of 1
+   bit.  Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
+   for this information */
+
+#define expand(W,i) ( W[ i & 15 ] = \
+                     ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
+                                W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )
+
+
+/* The prototype SHA sub-round.  The fundamental sub-round is:
+
+        a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
+        b' = a;
+        c' = ROTL( 30, b );
+        d' = c;
+        e' = d;
+
+   but this is implemented by unrolling the loop 5 times and renaming the
+   variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
+   This code is then replicated 20 times for each of the 4 functions, using
+   the next 20 values from the W[] array each time */
+
+#define subRound(a, b, c, d, e, f, k, data) \
+    ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
+
+/* Initialize the SHA values */
+
+void sha_init(struct sha_ctx *ctx)
+{
+       /* Set the h-vars to their initial values */
+       ctx->digest[ 0 ] = h0init;
+       ctx->digest[ 1 ] = h1init;
+       ctx->digest[ 2 ] = h2init;
+       ctx->digest[ 3 ] = h3init;
+       ctx->digest[ 4 ] = h4init;
+
+       /* Initialize bit count */
+       ctx->count_l = ctx->count_h = 0;
+  
+       /* Initialize buffer */
+       ctx->index = 0;
+}
+
+/* Perform the SHA transformation.  Note that this code, like MD5, seems to
+   break some optimizing compilers due to the complexity of the expressions
+   and the size of the basic block.  It may be necessary to split it into
+   sections, e.g. based on the four subrounds
+
+   Note that this function destroys the data area */
+
+static void sha_transform(struct sha_ctx *ctx, unsigned int *data )
+{
+       register unsigned int A, B, C, D, E;     /* Local vars */
+
+       /* Set up first buffer and local data buffer */
+       A = ctx->digest[0];
+       B = ctx->digest[1];
+       C = ctx->digest[2];
+       D = ctx->digest[3];
+       E = ctx->digest[4];
+
+       /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
+       subRound( A, B, C, D, E, f1, K1, data[ 0] );
+       subRound( E, A, B, C, D, f1, K1, data[ 1] );
+       subRound( D, E, A, B, C, f1, K1, data[ 2] );
+       subRound( C, D, E, A, B, f1, K1, data[ 3] );
+       subRound( B, C, D, E, A, f1, K1, data[ 4] );
+       subRound( A, B, C, D, E, f1, K1, data[ 5] );
+       subRound( E, A, B, C, D, f1, K1, data[ 6] );
+       subRound( D, E, A, B, C, f1, K1, data[ 7] );
+       subRound( C, D, E, A, B, f1, K1, data[ 8] );
+       subRound( B, C, D, E, A, f1, K1, data[ 9] );
+       subRound( A, B, C, D, E, f1, K1, data[10] );
+       subRound( E, A, B, C, D, f1, K1, data[11] );
+       subRound( D, E, A, B, C, f1, K1, data[12] );
+       subRound( C, D, E, A, B, f1, K1, data[13] );
+       subRound( B, C, D, E, A, f1, K1, data[14] );
+       subRound( A, B, C, D, E, f1, K1, data[15] );
+       subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) );
+       subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) );
+       subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) );
+       subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) );
+
+       subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) );
+       subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) );
+       subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) );
+       subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) );
+       subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) );
+       subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) );
+       subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) );
+       subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) );
+       subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) );
+       subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) );
+       subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) );
+       subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) );
+       subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) );
+       subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) );
+       subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) );
+       subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) );
+       subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) );
+       subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) );
+       subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) );
+       subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) );
+
+       subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) );
+       subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) );
+       subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) );
+       subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) );
+       subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) );
+       subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) );
+       subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) );
+       subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) );
+       subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) );
+       subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) );
+       subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) );
+       subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) );
+       subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) );
+       subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) );
+       subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) );
+       subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) );
+       subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) );
+       subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) );
+       subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) );
+       subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) );
+
+       subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) );
+       subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) );
+       subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) );
+       subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) );
+       subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) );
+       subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) );
+       subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) );
+       subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) );
+       subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) );
+       subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) );
+       subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) );
+       subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) );
+       subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) );
+       subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) );
+       subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) );
+       subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) );
+       subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) );
+       subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) );
+       subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) );
+       subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) );
+
+       /* Build message digest */
+       ctx->digest[0] += A;
+       ctx->digest[1] += B;
+       ctx->digest[2] += C;
+       ctx->digest[3] += D;
+       ctx->digest[4] += E;
+}
+
+
+static void sha_block(struct sha_ctx *ctx, unsigned char *block)
+{
+       unsigned int data[SHA_DATALEN];
+       unsigned int i;
+  
+       /* Update block count */
+       if (!++ctx->count_l)
+       {
+               ++ctx->count_h;
+       }
+
+       /* Endian independent conversion */
+       for (i = 0; i<SHA_DATALEN; i++, block += 4)
+       {
+               data[i] = STRING2INT(block);
+       }
+
+       sha_transform(ctx, data);
+}
+
+void sha_update(struct sha_ctx *ctx, unsigned char *buffer, unsigned int len)
+{
+       if (ctx->index)
+       { /* Try to fill partial block */
+               unsigned int left = SHA_DATASIZE - ctx->index;
+               if (len < left)
+               {
+                       memcpy(ctx->block + ctx->index, buffer, len);
+                       ctx->index += len;
+                       return; /* Finished */
+               }
+               else
+               {
+                       memcpy(ctx->block + ctx->index, buffer, left);
+                       sha_block(ctx, ctx->block);
+                       buffer += left;
+                       len -= left;
+               }
+       }
+       while (len >= SHA_DATASIZE)
+       {
+               sha_block(ctx, buffer);
+               buffer += SHA_DATASIZE;
+               len -= SHA_DATASIZE;
+       }
+       if ((ctx->index = len))     /* This assignment is intended */
+       {
+               /* Buffer leftovers */
+               memcpy(ctx->block, buffer, len);
+       }
+}
+         
+/* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern
+   1 0* (64-bit count of bits processed, MSB-first) */
+
+void sha_final(struct sha_ctx *ctx)
+{
+       unsigned int data[SHA_DATALEN];
+       unsigned int i;
+       unsigned int words;
+  
+       i = ctx->index;
+       /* Set the first char of padding to 0x80.  This is safe since there is
+          always at least one byte free */
+       ctx->block[i++] = 0x80;
+
+       /* Fill rest of word */
+       for( ; i & 3; i++)
+       {
+               ctx->block[i] = 0;
+       }
+       /* i is now a multiple of the word size 4 */
+       words = i >> 2;
+       for (i = 0; i < words; i++)
+       {
+               data[i] = STRING2INT(ctx->block + 4*i);
+       }
+
+       if (words > (SHA_DATALEN-2))
+       { /* No room for length in this block. Process it and
+          * pad with another one */
+               for (i = words ; i < SHA_DATALEN; i++)
+               {
+                       data[i] = 0;
+               }
+               sha_transform(ctx, data);
+               for (i = 0; i < (SHA_DATALEN-2); i++)
+               {
+                       data[i] = 0;
+               }
+       }
+       else
+       {
+               for (i = words ; i < SHA_DATALEN - 2; i++)
+               {
+                       data[i] = 0;
+               }
+       }
+       /* Theres 512 = 2^9 bits in one block */
+       data[SHA_DATALEN-2] = (ctx->count_h << 9) | (ctx->count_l >> 23);
+       data[SHA_DATALEN-1] = (ctx->count_l << 9) | (ctx->index << 3);
+       sha_transform(ctx, data);
+}
+
+void sha_digest(struct sha_ctx *ctx, unsigned char *s)
+{
+       unsigned int i;
+
+       if (s!=NULL)
+       {
+               for (i = 0; i < SHA_DIGESTLEN; i++)
+               {
+                       *s++ =         ctx->digest[i] >> 24;
+                       *s++ = 0xff & (ctx->digest[i] >> 16);
+                       *s++ = 0xff & (ctx->digest[i] >> 8);
+                       *s++ = 0xff &  ctx->digest[i];
+               }
+       }
+}
+
+unsigned char *sha(unsigned char *buffer, unsigned int len)
+{
+       static unsigned char buf[SHA_DIGESTSIZE];
+       SHA_CTX s;
+       sha_init(&s);
+       sha_update(&s, buffer, len);
+       sha_final(&s);
+       sha_digest(&s, buf);
+       return buf;
+}
diff --git a/sha1.h b/sha1.h
new file mode 100644 (file)
index 0000000..79dd900
--- /dev/null
+++ b/sha1.h
@@ -0,0 +1,29 @@
+#define SHA_DATASIZE    64
+#define SHA_DATALEN     16
+#define SHA_DIGESTSIZE  20
+#define SHA_DIGESTLEN    5
+/* The structure for storing SHA info */
+
+typedef struct sha_ctx {
+  unsigned int digest[SHA_DIGESTLEN];  /* Message digest */
+  unsigned int count_l, count_h;       /* 64-bit block count */
+  unsigned char block[SHA_DATASIZE];     /* SHA data buffer */
+  unsigned int index;                  /* index into buffer */
+} SHA_CTX;
+
+void sha_init(struct sha_ctx *ctx);
+void sha_update(struct sha_ctx *ctx, unsigned char *buffer, unsigned int len);
+void sha_final(struct sha_ctx *ctx);
+void sha_digest(struct sha_ctx *ctx, unsigned char *s);
+void sha_copy(struct sha_ctx *dest, struct sha_ctx *src);
+
+#ifndef EXTRACT_UCHAR
+#define EXTRACT_UCHAR(p)  (*(unsigned char *)(p))
+#endif
+
+#define STRING2INT(s) ((((((EXTRACT_UCHAR(s) << 8)    \
+                        | EXTRACT_UCHAR(s+1)) << 8)  \
+                        | EXTRACT_UCHAR(s+2)) << 8)  \
+                        | EXTRACT_UCHAR(s+3))
+
+unsigned char *sha(unsigned char *buffer, unsigned int len);