add outcome computation with test

also:
- enhance smc_zkp_2dle: secret can now be auto generated.
- enhance sum functions: can now use custom step advancing.
- add init1 and free1 for 1-dimensional point arrays.
- declare loop variables inside loop header.
- narrow some variable scopes.
This commit is contained in:
Markus Teich 2016-06-28 16:29:18 +02:00
parent 5dbbef588d
commit fb2bf04d38
3 changed files with 342 additions and 78 deletions

382
crypto.c
View File

@ -239,17 +239,12 @@ void
mpi_serialize (struct ec_mpi *dst, gcry_mpi_t src)
{
size_t rsize = 0;
unsigned int nbits;
const void *vp;
char *cp = (char *)dst;
gcry_error_t rc;
if (gcry_mpi_get_flag (src, GCRYMPI_FLAG_OPAQUE))
{
/* Store opaque MPIs left aligned into the buffer. Used by Ed25519 point
* compression */
vp = gcry_mpi_get_opaque (src, &nbits);
{ /* Store opaque MPIs left aligned. Used by Ed25519 point compression */
unsigned int nbits;
const void *vp = gcry_mpi_get_opaque (src, &nbits);
brandt_assert (vp);
rsize = (nbits + 7) / 8;
if (rsize > sizeof (struct ec_mpi))
@ -259,8 +254,10 @@ mpi_serialize (struct ec_mpi *dst, gcry_mpi_t src)
memset (((char *)dst) + rsize, 0, sizeof (struct ec_mpi) - rsize);
}
else
{
/* Store regular MPIs as unsigned ints right aligned into the buffer. */
{ /* Store regular MPIs as unsigned ints right aligned into the buffer. */
char *cp = (char *)dst;
gcry_error_t rc;
rc = gcry_mpi_print (GCRYMPI_FMT_USG, (void *)dst,
sizeof (struct ec_mpi), &rsize, src);
brandt_assert_gpgerr (rc);
@ -365,6 +362,57 @@ ec_point_parse (gcry_mpi_point_t dst, const struct ec_mpi *src)
}
/**
* smc_free1 releases all points in @a dst and frees the memory
*
* @param[in,out] dst The 1 dimensional array to clean up
* @param[in] size1 size of the first dimension
*/
static void
smc_free1 (gcry_mpi_point_t *dst, uint16_t size1)
{
if (NULL == dst)
return;
for (uint16_t i = 0; i < size1; i++)
if (NULL != dst[i])
gcry_mpi_point_release (dst[i]);
free (dst);
}
/**
* smc_init1 creates a 1 dimensional array of curve points
*
* @param[in] size1 size of the first dimension
* @return a pointer to the array or NULL on error.
* If not used anymore use smc_free2 to reclaim the memory.
*/
static gcry_mpi_point_t *
smc_init1 (uint16_t size1)
{
gcry_mpi_point_t *ret;
if (NULL == (ret = calloc (size1, sizeof (*ret))))
{
weprintf ("could not alloc memory for 1 dimensional point array");
return NULL;
}
for (uint16_t i = 0; i < size1; i++)
{
if (NULL == (ret[i] = gcry_mpi_point_new (0)))
{
weprintf ("could not init point in 1 dimensional array. "
"out of memory?");
smc_free1 (ret, size1);
return NULL;
}
}
return ret;
}
/**
* smc_free2 releases all points in @a dst and frees the memory
*
@ -375,14 +423,11 @@ ec_point_parse (gcry_mpi_point_t dst, const struct ec_mpi *src)
static void
smc_free2 (gcry_mpi_point_t **dst, uint16_t size1, uint16_t size2)
{
uint16_t i;
uint16_t j;
if (NULL == dst)
return;
for (i = 0; i < size1; i++)
for (j = 0; j < size2; j++)
for (uint16_t i = 0; i < size1; i++)
for (uint16_t j = 0; j < size2; j++)
if (NULL != dst[i][j])
gcry_mpi_point_release (dst[i][j]);
free (dst);
@ -400,8 +445,6 @@ smc_free2 (gcry_mpi_point_t **dst, uint16_t size1, uint16_t size2)
static gcry_mpi_point_t **
smc_init2 (uint16_t size1, uint16_t size2)
{
uint16_t i;
uint16_t j;
gcry_mpi_point_t **ret;
gcry_mpi_point_t *data;
@ -412,10 +455,10 @@ smc_init2 (uint16_t size1, uint16_t size2)
}
data = (gcry_mpi_point_t *)&ret[size1];
for (i = 0; i < size1; i++)
for (uint16_t i = 0; i < size1; i++)
{
ret[i] = &data[i * size2];
for (j = 0; j < size2; j++)
for (uint16_t j = 0; j < size2; j++)
{
if (NULL == (ret[i][j] = gcry_mpi_point_new (0)))
{
@ -444,16 +487,12 @@ smc_free3 (gcry_mpi_point_t ***dst,
uint16_t size2,
uint16_t size3)
{
uint16_t i;
uint16_t j;
uint16_t k;
if (NULL == dst)
return;
for (i = 0; i < size1; i++)
for (j = 0; j < size2; j++)
for (k = 0; k < size3; k++)
for (uint16_t i = 0; i < size1; i++)
for (uint16_t j = 0; j < size2; j++)
for (uint16_t k = 0; k < size3; k++)
if (NULL != dst[i][j][k])
gcry_mpi_point_release (dst[i][j][k]);
free (dst);
@ -472,9 +511,6 @@ smc_free3 (gcry_mpi_point_t ***dst,
static gcry_mpi_point_t ***
smc_init3 (uint16_t size1, uint16_t size2, uint16_t size3)
{
uint16_t i;
uint16_t j;
uint16_t k;
gcry_mpi_point_t ***ret;
gcry_mpi_point_t **layer1;
gcry_mpi_point_t *layer2;
@ -489,13 +525,13 @@ smc_init3 (uint16_t size1, uint16_t size2, uint16_t size3)
layer1 = (gcry_mpi_point_t **)&ret[size1];
layer2 = (gcry_mpi_point_t *)&layer1[size1 * size2];
for (i = 0; i < size1; i++)
for (uint16_t i = 0; i < size1; i++)
{
ret[i] = &layer1[i * size2];
for (j = 0; j < size2; j++)
for (uint16_t j = 0; j < size2; j++)
{
layer1[i * size2 + j] = &layer2[(i * size2 + j) * size3];
for (k = 0; k < size3; k++)
for (uint16_t k = 0; k < size3; k++)
{
if (NULL == (ret[i][j][k] = gcry_mpi_point_new (0)))
{
@ -515,23 +551,26 @@ smc_init3 (uint16_t size1, uint16_t size2, uint16_t size3)
* smc_sums_partial calculates sums up until the current index and stores them
* in @a out. \f$\forall i \leq len: out_i=\sum_{h=1}^iin_h\f$
*
* @param[out] out Where to store the resulting sums. Points may be given
* uninitialized, but the appropriate amount of memory has to be allocated
* beforehand.
* @param[out] out Where to store the resulting sums. Points must already be
* initialized beforehand.
* @param[in] in Input points.
* @param[in] len The length of both @a out and @a in.
* @param[in] len The length of @a out. @a in must be at least @a step times @a
* len elements long.
* @param[in] stepi The amount of items to advance in @a in each step. Can be
* used to sum over multi-dimensional arrays.
* @param[in] stepo The amount of items to advance in @a out each step. Can be
* used to store the sum in multi-dimensional arrays.
*/
static void
smc_sums_partial (gcry_mpi_point_t out[], gcry_mpi_point_t in[], uint16_t len)
smc_sums_partial (gcry_mpi_point_t out[],
gcry_mpi_point_t in[],
uint16_t len,
uint16_t stepi,
uint16_t stepo)
{
uint16_t i;
for (i = 0; i < len; i++)
{
out[i] = gcry_mpi_point_new (0);
gcry_mpi_ec_add (out[i], in[i], (i ? out[i - 1] : ec_zero), ec_ctx);
brandt_assert (NULL != out[i]);
}
brandt_assert (NULL != out);
for (uint16_t i = 0, o = 0; o < len * stepo; i += stepi, o += stepo)
gcry_mpi_ec_add (out[o], (o ? out[o - stepo] : ec_zero), in[i], ec_ctx);
}
@ -541,17 +580,21 @@ smc_sums_partial (gcry_mpi_point_t out[], gcry_mpi_point_t in[], uint16_t len)
*
* @param[out] out Where to store the result
* @param[in] in Input points.
* @param[in] len The length of @a in.
* @param[in] len The amount of summands to use from @a in. @a in must be at
* least @a step times @a len elements long.
* @param[in] step The amount of items to advance in @a in each step. Can be
* used to sum over multi-dimensional arrays.
*/
static void
smc_sum (gcry_mpi_point_t out, gcry_mpi_point_t in[], uint16_t len)
smc_sum (gcry_mpi_point_t out,
gcry_mpi_point_t in[],
uint16_t len,
uint16_t step)
{
uint16_t i;
brandt_assert (NULL != out);
/**\todo: how to copy a point more efficiently? */
gcry_mpi_ec_add (out, ec_zero, ec_zero, ec_ctx);
for (i = 0; i < len; i++)
for (uint16_t i = 0; i < len; i += step)
gcry_mpi_ec_add (out, out, in[i], ec_ctx);
}
@ -567,7 +610,6 @@ smc_sum (gcry_mpi_point_t out, gcry_mpi_point_t in[], uint16_t len)
unsigned char *
smc_gen_keyshare (struct AuctionData *ad, size_t *buflen)
{
uint16_t i;
unsigned char *ret;
struct proof_dl *proof1;
@ -581,7 +623,7 @@ smc_gen_keyshare (struct AuctionData *ad, size_t *buflen)
}
proof1 = (struct proof_dl *)(ret + sizeof (struct ec_mpi));
for (i = 0; i < ad->n; i++)
for (uint16_t i = 0; i < ad->n; i++)
ad->y[i] = gcry_mpi_point_new (0);
ad->x = gcry_mpi_new (0);
@ -596,7 +638,7 @@ int
smc_recv_keyshare (struct AuctionData *ad,
unsigned char *buf,
size_t buflen,
uint16_t sender_index)
uint16_t sender)
{
int ret = 0;
struct proof_dl *proof1;
@ -619,7 +661,7 @@ smc_recv_keyshare (struct AuctionData *ad,
}
/**\todo: how to copy a point more efficiently? */
gcry_mpi_ec_add (ad->y[sender_index], ec_zero, y, ec_ctx);
gcry_mpi_ec_add (ad->y[sender], ec_zero, y, ec_ctx);
ret = 1;
quit:
@ -637,7 +679,6 @@ quit:
unsigned char *
smc_encrypt_bid (struct AuctionData *ad, size_t *buflen)
{
uint16_t j;
unsigned char *ret;
unsigned char *cur;
struct proof_0og *proof3;
@ -658,12 +699,12 @@ smc_encrypt_bid (struct AuctionData *ad, size_t *buflen)
}
ad->Y = gcry_mpi_point_new (0);
smc_sum (ad->Y, ad->y, ad->n);
smc_sum (ad->Y, ad->y, ad->n, 1);
r_sum = gcry_mpi_new (0);
r_part = gcry_mpi_new (0);
for (j = 0; j < ad->k; j++)
for (uint16_t j = 0; j < ad->k; j++)
{
proof3 = (struct proof_0og *)(cur + 2 * sizeof (struct ec_mpi));
smc_zkp_0og (j == ad->b,
@ -690,10 +731,9 @@ int
smc_recv_encrypted_bid (struct AuctionData *ad,
unsigned char *buf,
size_t buflen,
uint16_t sender_index)
uint16_t sender)
{
int ret = 0;
uint16_t j;
unsigned char *cur = buf;
struct proof_0og *proof3;
gcry_mpi_point_t **ct; /* ciphertexts */
@ -713,7 +753,7 @@ smc_recv_encrypted_bid (struct AuctionData *ad,
gcry_mpi_ec_mul (alpha_sum, GCRYMPI_CONST_ONE, ec_zero, ec_ctx);
gcry_mpi_ec_mul (beta_sum, GCRYMPI_CONST_ONE, ec_zero, ec_ctx);
for (j = 0; j < ad->k; j++)
for (uint16_t j = 0; j < ad->k; j++)
{
ec_point_parse (ct[0][j], (struct ec_mpi *)cur);
ec_point_parse (ct[1][j], &((struct ec_mpi *)cur)[1]);
@ -739,11 +779,11 @@ smc_recv_encrypted_bid (struct AuctionData *ad,
goto quit;
}
for (j = 0; j < ad->k; j++)
for (uint16_t j = 0; j < ad->k; j++)
{
/**\todo: how to copy a point more efficiently? */
gcry_mpi_ec_add (ad->alpha[sender_index][j], ec_zero, ct[0][j], ec_ctx);
gcry_mpi_ec_add (ad->beta[sender_index][j], ec_zero, ct[1][j], ec_ctx);
gcry_mpi_ec_add (ad->alpha[sender][j], ec_zero, ct[0][j], ec_ctx);
gcry_mpi_ec_add (ad->beta[sender][j], ec_zero, ct[1][j], ec_ctx);
}
smc_free2 (ct, 2, ad->k);
@ -759,21 +799,201 @@ quit:
* smc_compute_outcome \todo
*
* @param ad TODO
* @param buflen TODO
*/
void
smc_compute_outcome (struct AuctionData *ad)
unsigned char *
smc_compute_outcome (struct AuctionData *ad, size_t *buflen)
{
uint16_t i;
uint16_t j;
unsigned char *ret;
unsigned char *cur;
gcry_mpi_point_t tmpa = gcry_mpi_point_new (0);
gcry_mpi_point_t tmpb = gcry_mpi_point_new (0);
gcry_mpi_point_t *tlta1;
gcry_mpi_point_t *tltb1;
gcry_mpi_point_t **tlta2;
gcry_mpi_point_t **tltb2;
gcry_mpi_point_t **tlta3;
gcry_mpi_point_t **tltb3;
struct ec_mpi *gamma;
struct ec_mpi *delta;
struct proof_2dle *proof2;
// create temporary table with partial sums
brandt_assert (ad && buflen);
for (i = 0; i < ad->n; i++)
*buflen = (ad->n * ad->k * /* nk * (gamma, delta, proof2) */
(sizeof (*gamma) + sizeof (*delta) + sizeof (*proof2)));
if (NULL == (cur = (ret = calloc (1, *buflen))) ||
NULL == (ad->gamma = smc_init3 (ad->n, ad->n, ad->k)) ||
NULL == (ad->delta = smc_init3 (ad->n, ad->n, ad->k)))
{
weprintf ("unable to alloc memory for first price outcome computation");
return NULL;
}
/*\todo ZKP*/
/* create temporary lookup tables with partial sums */
tlta1 = smc_init1 (ad->k);
tltb1 = smc_init1 (ad->k);
tlta2 = smc_init2 (ad->n, ad->k);
tltb2 = smc_init2 (ad->n, ad->k);
tlta3 = smc_init2 (ad->n, ad->k);
tltb3 = smc_init2 (ad->n, ad->k);
/* temporary lookup table for first summand (no one has a higher bid) */
for (uint16_t i = 0; i < ad->n; i++)
{
smc_sums_partial (tlta2[i], ad->alpha[i], ad->k, 1, 1);
smc_sums_partial (tltb2[i], ad->beta[i], ad->k, 1, 1);
for (uint16_t j = 0; j < ad->k; j++)
{
gcry_mpi_ec_sub (tlta3[i][j],
tlta2[i][ad->k - 1],
tlta2[i][j],
ec_ctx);
gcry_mpi_ec_sub (tltb3[i][j],
tltb2[i][ad->k - 1],
tltb2[i][j],
ec_ctx);
}
brandt_assert (!ec_point_cmp (ec_zero, tlta3[i][ad->k - 1]));
brandt_assert (!ec_point_cmp (ec_zero, tltb3[i][ad->k - 1]));
}
for (uint16_t j = 0; j < ad->k; j++)
{
smc_sum (tlta1[j], &tlta3[0][j], ad->n, ad->k);
smc_sum (tltb1[j], &tltb3[0][j], ad->n, ad->k);
}
brandt_assert (!ec_point_cmp (ec_zero, tlta1[ad->k - 1]));
brandt_assert (!ec_point_cmp (ec_zero, tltb1[ad->k - 1]));
/* \todo: merge into one nested i,j loop and one nested j,i loop? */
/* temporary lookup table for second summand (my bid is not lower) */
for (uint16_t i = 0; i < ad->n; i++)
{
for (uint16_t j = 0; j < ad->k; j++)
{
gcry_mpi_ec_sub (tlta2[i][j], tlta2[i][j], ad->alpha[i][j], ec_ctx);
gcry_mpi_ec_sub (tltb2[i][j], tltb2[i][j], ad->beta[i][j], ec_ctx);
}
brandt_assert (!ec_point_cmp (ec_zero, tlta2[i][0]));
brandt_assert (!ec_point_cmp (ec_zero, tltb2[i][0]));
}
/* temporary lookup table for third summand (no one with a lower index has
* the same bid) */
for (uint16_t j = 0; j < ad->k; j++)
{
smc_sums_partial (&tlta3[0][j], &ad->alpha[0][j], ad->n, ad->k, ad->k);
smc_sums_partial (&tltb3[0][j], &ad->beta[0][j], ad->n, ad->k, ad->k);
for (uint16_t i = 0; i < ad->n; i++)
{
gcry_mpi_ec_sub (tlta3[i][j], tlta3[i][j], ad->alpha[i][j], ec_ctx);
gcry_mpi_ec_sub (tltb3[i][j], tltb3[i][j], ad->beta[i][j], ec_ctx);
}
brandt_assert (!ec_point_cmp (ec_zero, tlta3[0][j]));
brandt_assert (!ec_point_cmp (ec_zero, tltb3[0][j]));
}
for (uint16_t i = 0; i < ad->n; i++)
{
for (uint16_t j = 0; j < ad->k; j++)
{
gamma = (struct ec_mpi *)cur;
delta = &((struct ec_mpi *)cur)[1];
proof2 = (struct proof_2dle *)(cur + 2 * sizeof (struct ec_mpi));
/* compute inner gamma */
gcry_mpi_ec_add (tmpa, tlta1[j], tlta2[i][j], ec_ctx);
gcry_mpi_ec_add (tmpa, tmpa, tlta3[i][j], ec_ctx);
/* compute inner delta */
gcry_mpi_ec_add (tmpb, tltb1[j], tltb2[i][j], ec_ctx);
gcry_mpi_ec_add (tmpb, tmpb, tltb3[i][j], ec_ctx);
/* copy unmasked outcome to all other bidder layers so they don't
* have to be recomputed to check the ZK proof_2dle's from other
* bidders when receiving their outcome messages */
for (uint16_t a = 0; a < ad->n; a++)
{
/**\todo: how to copy a point more efficiently? */
gcry_mpi_ec_add (ad->gamma[a][i][j], ec_zero, tmpa, ec_ctx);
gcry_mpi_ec_add (ad->delta[a][i][j], ec_zero, tmpb, ec_ctx);
}
/* apply random masking for losing bidders */
smc_zkp_2dle (ad->gamma[ad->i][i][j],
ad->delta[ad->i][i][j],
tmpa,
tmpb,
NULL,
proof2);
ec_point_serialize (gamma, ad->gamma[ad->i][i][j]);
ec_point_serialize (delta, ad->delta[ad->i][i][j]);
cur += sizeof (*gamma) + sizeof (*delta) + sizeof (*proof2);
}
}
gcry_mpi_point_release (tmpa);
gcry_mpi_point_release (tmpb);
smc_free1 (tlta1, ad->n);
smc_free1 (tltb1, ad->n);
smc_free2 (tlta2, ad->n, ad->k);
smc_free2 (tltb2, ad->n, ad->k);
smc_free2 (tlta3, ad->n, ad->k);
smc_free2 (tltb3, ad->n, ad->k);
return ret;
}
int
smc_recv_outcome (struct AuctionData *ad,
unsigned char *buf,
size_t buflen,
uint16_t sender)
{
int ret = 0;
unsigned char *cur = buf;
struct proof_2dle *proof2;
gcry_mpi_point_t gamma = gcry_mpi_point_new (0);
gcry_mpi_point_t delta = gcry_mpi_point_new (0);
brandt_assert (ad && buf);
if (buflen != (ad->n * ad->k *
(2 * sizeof (struct ec_mpi) + sizeof (*proof2))))
{
weprintf ("wrong size of received encrypted bid");
goto quit;
}
for (uint16_t i = 0; i < ad->n; i++)
{
for (uint16_t j = 0; j < ad->k; j++)
{
ec_point_parse (gamma, (struct ec_mpi *)cur);
ec_point_parse (delta, &((struct ec_mpi *)cur)[1]);
proof2 = (struct proof_2dle *)(cur + 2 * sizeof (struct ec_mpi));
if (smc_zkp_2dle_check (gamma,
delta,
ad->gamma[sender][i][j],
ad->delta[sender][i][j],
proof2))
{
weprintf ("wrong zkp2 for gamma, delta received");
goto quit;
}
gcry_mpi_ec_add (ad->gamma[sender][i][j], gamma, ec_zero, ec_ctx);
gcry_mpi_ec_add (ad->delta[sender][i][j], delta, ec_zero, ec_ctx);
cur += 2 * sizeof (struct ec_mpi) + sizeof (*proof2);
}
}
ret = 1;
quit:
gcry_mpi_point_release (gamma);
gcry_mpi_point_release (delta);
return ret;
}
@ -885,7 +1105,8 @@ smc_zkp_dl_check (const gcry_mpi_point_t v,
* Must be known to the verifier.
* @param[in] g1 first base point. Must be known to the verifier.
* @param[in] g2 second base point. Must be known to the verifier.
* @param[in] x private number to prove knowledge of.
* @param[in] x private number to prove knowledge of. May be NULL if not used by
* the caller.
* @param[out] proof pointer where to save the output proof structure. Must be
* shared with the verifier.
*/
@ -901,6 +1122,7 @@ smc_zkp_2dle (gcry_mpi_point_t v,
struct brandt_hash_code challhash;
gcry_mpi_point_t rv;
gcry_mpi_point_t rw;
gcry_mpi_t rx;
gcry_mpi_point_t a = gcry_mpi_point_new (0);
gcry_mpi_point_t b = gcry_mpi_point_new (0);
gcry_mpi_t r = gcry_mpi_new (0);
@ -909,12 +1131,16 @@ smc_zkp_2dle (gcry_mpi_point_t v,
rv = (NULL == v) ? gcry_mpi_point_new (0) : v;
rw = (NULL == w) ? gcry_mpi_point_new (0) : w;
rx = (NULL == x) ? gcry_mpi_new (0) : x;
if (NULL == x)
ec_skey_create (rx);
/* v = x*g1 */
gcry_mpi_ec_mul (rv, x, g1, ec_ctx);
gcry_mpi_ec_mul (rv, rx, g1, ec_ctx);
/* w = x*g2 */
gcry_mpi_ec_mul (rw, x, g2, ec_ctx);
gcry_mpi_ec_mul (rw, rx, g2, ec_ctx);
/* a = z*g1 */
ec_keypair_create_base (a, z, g1);
@ -934,7 +1160,7 @@ smc_zkp_2dle (gcry_mpi_point_t v,
gcry_mpi_mod (c, c, ec_n);
/* r = z + cx */
gcry_mpi_mulm (r, c, x, ec_n);
gcry_mpi_mulm (r, c, rx, ec_n);
gcry_mpi_addm (r, r, z, ec_n);
mpi_serialize (&proof->r, r);
@ -945,6 +1171,8 @@ smc_zkp_2dle (gcry_mpi_point_t v,
gcry_mpi_point_release (rv);
if (NULL == w)
gcry_mpi_point_release (rw);
if (NULL == x)
gcry_mpi_release (rx);
gcry_mpi_point_release (a);
gcry_mpi_point_release (b);
gcry_mpi_release (r);

View File

@ -123,4 +123,9 @@ int smc_recv_encrypted_bid (struct AuctionData *ad,
size_t buflen,
uint16_t sender_index);
unsigned char *smc_compute_outcome (struct AuctionData *ad, size_t *buflen);
int smc_recv_outcome (struct AuctionData *ad,
unsigned char *buf,
size_t buflen,
uint16_t sender);
#endif /* ifndef _BRANDT_CRYPTO_H */

View File

@ -257,6 +257,36 @@ test_round1 ()
}
int
test_round2 ()
{
uint16_t i, s;
unsigned char *bufs[bidders];
size_t lens[bidders];
for (i = 0; i < bidders; i++)
{
bufs[i] = smc_compute_outcome (&ad[i], &lens[i]);
check (bufs[i], "failed to encrypt bid");
}
for (i = 0; i < bidders; i++)
{
for (s = 0; s < bidders; s++)
{
if (s == i)
continue;
check (smc_recv_outcome (&ad[i], bufs[s], lens[s], s),
"failed checking outcome");
}
}
for (i = 0; i < bidders; i++)
free (bufs[i]);
return 1;
}
void
cleanup_auction_data ()
{
@ -299,6 +329,7 @@ main (int argc, char *argv[])
run (test_setup_auction_data);
run (test_prologue);
run (test_round1);
run (test_round2);
cleanup_auction_data ();
}