static void point_random(element_t a) { point_ptr p = DATA(a); element_ptr x = p->x, y = p->y; field_ptr f = x->field; p->isinf = 0; element_t t, t2, e1; element_init(t, f); element_init(e1, f); element_set1(e1); element_init(t2, f); do { element_random(x); if (element_is0(x)) continue; element_cubic(t, x); // t == x^3 element_sub(t, t, x); // t == x^3 - x element_add(t, t, e1); // t == x^3 - x + 1 element_sqrt(y, t); // y == sqrt(x^3 - x + 1) element_mul(t2, y, y); // t2 == x^3 - x + 1 } while (element_cmp(t2, t)); // t2 != t // make sure order of $a$ is order of $G_1$ pairing_ptr pairing = FIELD(a)->pairing; pairing_data_ptr dp = pairing->data; element_pow_mpz(a, a, dp->n2); element_clear(t); element_clear(t2); element_clear(e1); }
static val_ptr v_field_cast(val_ptr v, tree_ptr t) { // TODO: Check args, x is an element. val_ptr x = tree_eval((tree_ptr)darray_at(t->child, 0)); element_ptr e = x->elem; if (e->field == M) { if (v->field == M) return x; element_ptr e2 = element_new(v->field); if (element_is0(e)) // if 'set0' is not 'set1' in base field of GT, but we hope 'GT(0)' calls 'set1', we may directly call 'element_set0' here element_set0(e2); else if (element_is1(e)) // reason is same as above element_set1(e2); else element_set_multiz(e2, (multiz)e->data); x->elem = e2; return x; } if (v->field == M) { // Map to/from integer. TODO: Map to/from multiz instead. mpz_t z; mpz_init(z); element_to_mpz(z, e); element_clear(e); element_init(e, v->field); element_set_mpz(e, z); mpz_clear(z); } return x; }
static void sn_add(element_t c, element_t a, element_t b) { point_ptr r = c->data; point_ptr p = a->data; point_ptr q = b->data; if (p->inf_flag) { sn_set(c, b); return; } if (q->inf_flag) { sn_set(c, a); return; } if (!element_cmp(p->x, q->x)) { if (!element_cmp(p->y, q->y)) { if (element_is0(p->y)) { r->inf_flag = 1; return; } else { sn_double_no_check(r, p); return; } } //points are inverses of each other r->inf_flag = 1; return; } else { element_t lambda, e0, e1; element_init(lambda, p->x->field); element_init(e0, p->x->field); element_init(e1, p->x->field); //lambda = (y2-y1)/(x2-x1) element_sub(e0, q->x, p->x); element_invert(e0, e0); element_sub(lambda, q->y, p->y); element_mul(lambda, lambda, e0); //x3 = lambda^2 - x1 - x2 - 1 element_square(e0, lambda); element_sub(e0, e0, p->x); element_sub(e0, e0, q->x); element_set1(e1); element_sub(e0, e0, e1); //y3 = (x1-x3)lambda - y1 element_sub(e1, p->x, e0); element_mul(e1, e1, lambda); element_sub(e1, e1, p->y); element_set(r->x, e0); element_set(r->y, e1); r->inf_flag = 0; element_clear(lambda); element_clear(e0); element_clear(e1); } }
static void curve_mul(element_ptr c, element_ptr a, element_ptr b) { curve_data_ptr cdp = (curve_data_ptr)a->field->data; point_ptr r = (point_ptr)c->data, p = (point_ptr)a->data, q = (point_ptr)b->data; if (p->inf_flag) { curve_set(c, b); return; } if (q->inf_flag) { curve_set(c, a); return; } if (!element_cmp(p->x, q->x)) { if (!element_cmp(p->y, q->y)) { if (element_is0(p->y)) { r->inf_flag = 1; return; } else { double_no_check(r, p, cdp->a); return; } } //points are inverses of each other r->inf_flag = 1; return; } else { element_t lambda, e0, e1; element_init(lambda, cdp->field); element_init(e0, cdp->field); element_init(e1, cdp->field); //lambda = (y2-y1)/(x2-x1) element_sub(e0, q->x, p->x); element_invert(e0, e0); element_sub(lambda, q->y, p->y); element_mul(lambda, lambda, e0); //x3 = lambda^2 - x1 - x2 element_square(e0, lambda); element_sub(e0, e0, p->x); element_sub(e0, e0, q->x); //y3 = (x1-x3)lambda - y1 element_sub(e1, p->x, e0); element_mul(e1, e1, lambda); element_sub(e1, e1, p->y); element_set(r->x, e0); element_set(r->y, e1); r->inf_flag = 0; element_clear(lambda); element_clear(e0); element_clear(e1); } }
static element_ptr curve_item(element_ptr e, int i) { if (element_is0(e)) return NULL; point_ptr P = (point_ptr)e->data; switch(i) { case 0: return P->x; case 1: return P->y; default: return NULL; } }
static val_ptr eval_ternary(tree_ptr t) { val_ptr x = tree_eval((tree_ptr)darray_at(t->child, 0)); if (v_error == x->type) { return x; } if (x->type != v_elem) { return val_new_error("element expected in ternary operator"); } if (!element_is0(x->elem)) { return tree_eval((tree_ptr)darray_at(t->child, 1)); } return tree_eval((tree_ptr)darray_at(t->child, 2)); }
static void sn_double(element_t c, element_t a) { point_ptr r = c->data; point_ptr p = a->data; if (p->inf_flag) { r->inf_flag = 1; return; } if (element_is0(p->y)) { r->inf_flag = 1; return; } sn_double_no_check(r, p); }
static void curve_double(element_ptr c, element_ptr a) { curve_data_ptr cdp = (curve_data_ptr)a->field->data; point_ptr r = (point_ptr)c->data, p = (point_ptr)a->data; if (p->inf_flag) { r->inf_flag = 1; return; } if (element_is0(p->y)) { r->inf_flag = 1; return; } double_no_check(r, p, cdp->a); }
//singular with node: y^2 = x^3 + x^2 static void sn_random(element_t a) { point_ptr p = a->data; element_t t; element_init(t, p->x->field); p->inf_flag = 0; do { element_random(p->x); if (element_is0(p->x)) continue; element_square(t, p->x); element_add(t, t, p->x); element_mul(t, t, p->x); } while (!element_is_sqr(t)); element_sqrt(p->y, t); element_clear(t); }
static void tate_18(element_ptr out, element_ptr P, element_ptr Q, element_ptr R, element_ptr S) { mpz_t pow; element_t PR; element_t QS; element_init(PR, P->field); element_init(QS, P->field); element_t outd; element_init(outd, out->field); mpz_init(pow); mpz_set_ui(pow, (19*19-1)/18); element_add(PR, P, R); element_add(QS, Q, S); if (element_is0(QS)) { element_t S2; element_init(S2, P->field); element_double(S2, S); miller(out, PR, S, S2, 18); miller(outd, R, S, S2, 18); element_clear(S2); } else { miller(out, PR, QS, S, 18); miller(outd, R, QS, S, 18); } element_clear(PR); element_clear(QS); element_invert(outd, outd); element_mul(out, out, outd); element_pow_mpz(out, out, pow); element_clear(outd); mpz_clear(pow); }
/** * In this scheme every signer can aggregate a signature on a different message. * * @param sign the computated signature (param modified) * @param m the message to sign * @param mlen message length * * The __caller__ SHOULD add id:message to some storage as we need the aggregate signature * signers id=asnum to vrfy @see pbgp_ibe_vrfy() * * ie. pbgp_store_uput(store, STORE_KEY_SET_DATA(key, ASLIST, signer_id), (void *) m, mlen) * */ void pbgp_ibe_sign(setup_params_t *setup, ibe_keypair_t *key, const unsigned char *m, size_t mlen, ibe_signature_t *sign) { element_t ri, t1, t2, t3, t4, ci; // // Check if this is the first signature // "w must be an unique unseen value" // if (element_is0(sign->w)) { #if HAVE_LIBUUID || HAVE_LIBE2FS_UUID uuid_t uuid; uuid_generate(uuid); element_from_hash(sign->w, uuid, sizeof uuid); #else element_random(sign->w); #endif if (element_is0(sign->w)) { pbgp_fatal("pbgp_ibe_sign :: random"); } } element_init_G1(t1, setup->pairing); element_init_G1(t2, setup->pairing); element_init_G1(t3, setup->pairing); element_init_G1(t4, setup->pairing); element_init_Zr(ri, setup->pairing); element_init_Zr(ci, setup->pairing); // ci = binary_hash(message) unsigned char hash[EVP_MAX_MD_SIZE + 1]; memset(hash, 0, sizeof (hash)); unsigned int len = pbgp_rsa_uhash(m, mlen, hash); element_from_hash(ci, hash, len); // ri = random element element_random(ri); // ri * Pw element_mul_zn(t1, sign->w, ri); // Pi,0 = key->priv0 // Pi,1 = key->priv1 // ci * Pi,1 element_mul_zn(t2, key->priv1, ci); // ri * Pi,0 // element_mul(t5, ri, key->pub0); // ri * Pw + ci * sP(i,1) + sP(i,0) element_add(t3, t1, t2); // Si = ri * Pw + ci * Pi,1 + Pi,0 element_add(t3, t3, key->priv0); // Ti = ri * P element_mul_zn(t4, setup->g, ri); // Sum(sign) for each signature // w is the same for all signers // u = sum(Si) element_add(sign->u, sign->u, t3); // v = sum(Ti) element_add(sign->v, sign->v, t4); element_clear(t1); element_clear(t2); element_clear(t3); element_clear(t4); element_clear(ri); element_clear(ci); }
//compute c_i=a_i+b_i at one time. static void multi_add(element_ptr c[], element_ptr a[], element_ptr b[], int n){ int i; element_t* table = (element_t*)pbc_malloc(sizeof(element_t)*n); //a big problem? point_ptr p, q, r; element_t e0, e1, e2; curve_data_ptr cdp = (curve_data_ptr)a[0]->field->data; p = (point_ptr)a[0]->data; q = (point_ptr)b[0]->data; element_init(e0, p->x->field); element_init(e1, p->x->field); element_init(e2, p->x->field); element_init(table[0], p->x->field); element_sub(table[0], q->x, p->x); for(i=1; i<n; i++){ p = (point_ptr)a[i]->data; q = (point_ptr)b[i]->data; element_init(table[i], p->x->field); element_sub(table[i], q->x, p->x); element_mul(table[i], table[i], table[i-1]); } element_invert(e2, table[n-1]); for(i=n-1; i>0; i--){ p = (point_ptr)a[i]->data; q = (point_ptr)b[i]->data; element_mul(table[i], table[i-1], e2); element_sub(e1, q->x, p->x); element_mul(e2,e2,e1); //e2=e2*(x2_j-x1_j) } element_set(table[0],e2); //e2 no longer used. for(i=0; i<n; i++){ p = (point_ptr)a[i]->data; q = (point_ptr)b[i]->data; r = (point_ptr)c[i]->data; if (p->inf_flag) { curve_set(c[i], b[i]); continue; } if (q->inf_flag) { curve_set(c[i], a[i]); continue; } if (!element_cmp(p->x, q->x)) { //a[i]=b[i] if (!element_cmp(p->y, q->y)) { if (element_is0(p->y)) { r->inf_flag = 1; continue; } else { double_no_check(r, p, cdp->a); continue; } } //points are inverses of each other r->inf_flag = 1; continue; } else { //lambda = (y2-y1)/(x2-x1) element_sub(e2, q->y, p->y); element_mul(e2, e2, table[i]); //x3 = lambda^2 - x1 - x2 element_square(e0, e2); element_sub(e0, e0, p->x); element_sub(e0, e0, q->x); //y3 = (x1-x3)lambda - y1 element_sub(e1, p->x, e0); element_mul(e1, e1, e2); element_sub(e1, e1, p->y); element_set(r->x, e0); element_set(r->y, e1); r->inf_flag = 0; } } element_clear(e0); element_clear(e1); element_clear(e2); for(i=0; i<n; i++){ element_clear(table[i]); } pbc_free(table); }
//compute c_i=a_i+a_i at one time. static void multi_double(element_ptr c[], element_ptr a[], int n) { int i; element_t* table = (element_t*)pbc_malloc(sizeof(element_t)*n); //a big problem? element_t e0, e1, e2; point_ptr q, r; curve_data_ptr cdp = (curve_data_ptr)a[0]->field->data; q = (point_ptr)a[0]->data; element_init(e0,q->y->field); element_init(e1,q->y->field); element_init(e2,q->y->field); for(i=0; i<n; i++){ q = (point_ptr)a[i]->data; r = (point_ptr)c[i]->data; element_init(table[i],q->y->field); if (q->inf_flag) { r->inf_flag = 1; continue; } if (element_is0(q->y)) { r->inf_flag = 1; continue; } } //to compute 1/2y multi. see Cohen's GTM139 Algorithm 10.3.4 for(i=0; i<n; i++){ q = (point_ptr)a[i]->data; element_double(table[i],q->y); if(i>0) element_mul(table[i],table[i],table[i-1]); } element_invert(e2,table[n-1]); //ONLY ONE inv is required now. for(i=n-1; i>0; i--){ q = (point_ptr)a[i]->data; element_mul(table[i],table[i-1],e2); element_mul(e2,e2,q->y); element_double(e2,e2); //e2=e2*2y_j } element_set(table[0],e2); //e2 no longer used. for(i=0; i<n; i++){ q = (point_ptr)a[i]->data; r = (point_ptr)c[i]->data; if(r->inf_flag) continue; //e2=lambda = (3x^2 + a) / 2y element_square(e2, q->x); element_mul_si(e2, e2, 3); element_add(e2, e2, cdp->a); element_mul(e2, e2, table[i]); //Recall that table[i]=1/2y_i //x1 = lambda^2 - 2x element_double(e1, q->x); element_square(e0, e2); element_sub(e0, e0, e1); //y1 = (x - x1)lambda - y element_sub(e1, q->x, e0); element_mul(e1, e1, e2); element_sub(e1, e1, q->y); element_set(r->x, e0); element_set(r->y, e1); r->inf_flag = 0; } element_clear(e0); element_clear(e1); element_clear(e2); for(i=0; i<n; i++){ element_clear(table[i]); } pbc_free(table); }
static val_ptr run_CHECK(val_ptr v[]) { if (element_is0(v[0]->elem)) { pbc_die("CHECK failed"); } return v[0]; }
static int curve_item_count(element_ptr e) { if (element_is0(e)) { return 0; } return 2; }
void pbc_param_init_f_gen(pbc_param_t p, int bits) { f_init(p); f_param_ptr fp = p->data; //36 is a 6-bit number int xbit = (bits - 6) / 4; //TODO: use binary search to find smallest appropriate x mpz_t x, t; mpz_ptr q = fp->q; mpz_ptr r = fp->r; mpz_ptr b = fp->b; field_t Fq, Fq2, Fq2x; element_t e1; element_t f; field_t c; element_t P; mpz_init(x); mpz_init(t); mpz_setbit(x, xbit); for (;;) { mpz_mul(t, x, x); mpz_mul_ui(t, t, 6); mpz_add_ui(t, t, 1); tryminusx(q, x); mpz_sub(r, q, t); mpz_add_ui(r, r, 1); if (mpz_probab_prime_p(q, 10) && mpz_probab_prime_p(r, 10)) break; tryplusx(q, x); mpz_sub(r, q, t); mpz_add_ui(r, r, 1); if (mpz_probab_prime_p(q, 10) && mpz_probab_prime_p(r, 10)) break; mpz_add_ui(x, x, 1); } field_init_fp(Fq, q); element_init(e1, Fq); for (;;) { element_random(e1); field_init_curve_b(c, e1, r, NULL); element_init(P, c); element_random(P); element_mul_mpz(P, P, r); if (element_is0(P)) break; element_clear(P); field_clear(c); } element_to_mpz(b, e1); element_clear(e1); field_init_quadratic(Fq2, Fq); element_to_mpz(fp->beta, field_get_nqr(Fq)); field_init_poly(Fq2x, Fq2); element_init(f, Fq2x); // Find an irreducible polynomial of the form f = x^6 + alpha. // Call poly_set_coeff1() first so we can use element_item() for the other // coefficients. poly_set_coeff1(f, 6); for (;;) { element_random(element_item(f, 0)); if (poly_is_irred(f)) break; } //extend F_q^2 using f = x^6 + alpha //see if sextic twist contains a subgroup of order r //if not, it's the wrong twist: replace alpha with alpha^5 { field_t ctest; element_t Ptest; mpz_t z0, z1; mpz_init(z0); mpz_init(z1); element_init(e1, Fq2); element_set_mpz(e1, fp->b); element_mul(e1, e1, element_item(f, 0)); element_neg(e1, e1); field_init_curve_b(ctest, e1, r, NULL); element_init(Ptest, ctest); element_random(Ptest); //I'm not sure what the #E'(F_q^2) is, but //it definitely divides n_12 = #E(F_q^12). It contains a //subgroup of order r if and only if //(n_12 / r^2)P != O for some (in fact most) P in E'(F_q^6) mpz_pow_ui(z0, q, 12); mpz_add_ui(z0, z0, 1); pbc_mpz_trace_n(z1, q, t, 12); mpz_sub(z1, z0, z1); mpz_mul(z0, r, r); mpz_divexact(z1, z1, z0); element_mul_mpz(Ptest, Ptest, z1); if (element_is0(Ptest)) { mpz_set_ui(z0, 5); element_pow_mpz(element_item(f, 0), element_item(f, 0), z0); } element_clear(e1); element_clear(Ptest); field_clear(ctest); mpz_clear(z0); mpz_clear(z1); } element_to_mpz(fp->alpha0, element_x(element_item(f, 0))); element_to_mpz(fp->alpha1, element_y(element_item(f, 0))); element_clear(f); field_clear(Fq2x); field_clear(Fq2); field_clear(Fq); mpz_clear(t); mpz_clear(x); }
void pbc_param_init_e_gen(pbc_param_t par, int rbits, int qbits) { e_init(par); e_param_ptr p = par->data; //3 takes 2 bits to represent int hbits = (qbits - 2) / 2 - rbits; mpz_ptr q = p->q; mpz_ptr r = p->r; mpz_ptr h = p->h; mpz_t n; field_t Fq; field_t cc; element_t j; int found = 0; //won't find any curves is hbits is too low if (hbits < 3) hbits = 3; mpz_init(n); do { int i; mpz_set_ui(r, 0); if (rand() % 2) { p->exp2 = rbits - 1; p->sign1 = 1; } else { p->exp2 = rbits; p->sign1 = -1; } mpz_setbit(r, p->exp2); p->exp1 = (rand() % (p->exp2 - 1)) + 1; //use q as a temp variable mpz_set_ui(q, 0); mpz_setbit(q, p->exp1); if (p->sign1 > 0) { mpz_add(r, r, q); } else { mpz_sub(r, r, q); } if (rand() % 2) { p->sign0 = 1; mpz_add_ui(r, r, 1); } else { p->sign0 = -1; mpz_sub_ui(r, r, 1); } if (!mpz_probab_prime_p(r, 10)) continue; for (i=0; i<10; i++) { //use q as a temp variable mpz_set_ui(q, 0); mpz_setbit(q, hbits + 1); pbc_mpz_random(h, q); mpz_mul(h, h, h); mpz_mul_ui(h, h, 3); //finally q takes the value it should mpz_mul(n, r, r); mpz_mul(n, n, h); mpz_add_ui(q, n, 1); if (mpz_probab_prime_p(q, 10)) { found = 1; break; } } } while (!found); /* do { mpz_set_ui(r, 0); mpz_setbit(r, rbits); pbc_mpz_random(r, r); mpz_nextprime(r, r); mpz_mul(n, r, r); mpz_mul_ui(n, n, 3); mpz_add_ui(q, n, 1); } while (!mpz_probab_prime_p(q, 10)); */ field_init_fp(Fq, q); element_init(j, Fq); element_set_si(j, 1); field_init_curve_b(cc, j, n, NULL); element_clear(j); // We may need to twist it. { // Pick a random point P and twist the curve if P has the wrong order. element_t P; element_init(P, cc); element_random(P); element_mul_mpz(P, P, n); if (!element_is0(P)) field_reinit_curve_twist(cc); element_clear(P); } element_to_mpz(p->a, curve_field_a_coeff(cc)); element_to_mpz(p->b, curve_field_b_coeff(cc)); mpz_clear(n); }
static int fq_is1(element_ptr e) { eptr p = e->data; return element_is1(p->x) && element_is0(p->y); }
int claim_new_epoch(char *glb[2],char *epoch[2],char *facc,char *outf,setup_params_t *setup) { int rv = 0, i = 0; epoch_item_t *ep = NULL; id_list_t *act = NULL, *rvk = NULL; rv = newepoch_init(&ep,setup); if(rv < 0) { pbgp_error("claim_new_epoch :: Cannot complete initialization process\n"); goto out1; } rv = ids_init(&ep->epls.act,&ep->epls.rvk); rv -= ids_init(&act,&rvk); if (rv < 0) { pbgp_error("claim_new_epoch :: Cannot initialize ids lists\n"); goto out2; } if (!file_exists(epoch[0]) || !file_exists(epoch[1])) { rv = -1; pbgp_error("Cannot find epoch files (%s,%s)\n",epoch[0],epoch[1]); goto out2; } rv = ids_load(epoch[0],ep->epls.act); rv -= ids_load(epoch[1],ep->epls.rvk); rv -= ids_load(glb[0],act); rv -= ids_load(glb[1],rvk); if(rv < 0) { pbgp_error("claim_new_epoch :: Cannot load ids\n"); goto out3; } if(ep->epls.act->size == 0 && ep->epls.rvk->size == 0) { rv = -1; pbgp_error("claim_new_epoch :: There are no entities to join or revoke\n"); goto out3; } //if first epoch if(!file_exists(facc)) { acc_create(ep->acc); ep->epoch = 0; } else { rv = acc_load(facc,ep->acc); if(rv < 0) { pbgp_error("claim_new_epoch :: Cannot create accumulator\n"); goto out4; } } ep->epoch = ep->acc->nep; //element_printf("ACC: %B\n",ep->acc); newepoch_gen(outf,ep,setup); //save accumulator //element_printf("ACC: %B\n",ep->acc); if(element_is0(ep->acc->elem)) { printf("ACC is 0\n"); element_set1(ep->acc->elem); } acc_save(facc,ep->epoch,ep->acc); //update global joined entities for(i = 0; i < ep->epls.act->size; i++) { ids_add(act,&ep->epls.act->head[i]); } //update global revoked entities for(i = 0; i < ep->epls.rvk->size; i++) { ids_remove(act,&ep->epls.rvk->head[i]); ids_add(rvk,&ep->epls.rvk->head[i]); } ids_save(glb[0],act); ids_save(glb[1],rvk); //delete old epoch files (or write empty list) remove(epoch[0]); remove(epoch[1]); out4: acc_clear(ep->acc); out3: ids_clear(ep->epls.act,ep->epls.rvk); ids_clear(act,rvk); out2: newepoch_clear(ep); out1: return rv; }
// Computes a curve and sets fp to the field it is defined over using the // complex multiplication method, where cm holds the appropriate information // (e.g. discriminant, field order). static void compute_cm_curve(d_param_ptr param, pbc_cm_ptr cm) { element_t hp, root; field_t fp, fpx; field_t cc; field_init_fp(fp, cm->q); field_init_poly(fpx, fp); element_init(hp, fpx); mpz_t *coefflist; int n = (int)pbc_hilbert(&coefflist, cm->D); // Temporarily set the coefficient of x^{n-1} to 1 so hp has degree n - 1, // allowing us to use poly_coeff(). poly_set_coeff1(hp, n - 1); int i; for (i = 0; i < n; i++) { element_set_mpz(element_item(hp, i), coefflist[i]); } pbc_hilbert_free(coefflist, n); // TODO: Remove x = 0, 1728 roots. // TODO: What if there are no roots? //printf("hp "); //element_out_str(stdout, 0, hp); //printf("\n"); element_init(root, fp); poly_findroot(root, hp); //printf("root = "); //element_out_str(stdout, 0, root); //printf("\n"); element_clear(hp); field_clear(fpx); // The root is the j-invariant of the desired curve. field_init_curve_j(cc, root, cm->n, NULL); element_clear(root); // We may need to twist it. { // Pick a random point P and twist the curve if it has the wrong order. element_t P; element_init(P, cc); element_random(P); element_mul_mpz(P, P, cm->n); if (!element_is0(P)) field_reinit_curve_twist(cc); element_clear(P); } mpz_set(param->q, cm->q); mpz_set(param->n, cm->n); mpz_set(param->h, cm->h); mpz_set(param->r, cm->r); element_to_mpz(param->a, curve_field_a_coeff(cc)); element_to_mpz(param->b, curve_field_b_coeff(cc)); param->k = cm->k; { mpz_t z; mpz_init(z); // Compute order of curve in F_q^k. // n = q - t + 1 hence t = q - n + 1 mpz_sub(z, param->q, param->n); mpz_add_ui(z, z, 1); pbc_mpz_trace_n(z, param->q, z, param->k); mpz_pow_ui(param->nk, param->q, param->k); mpz_sub_ui(z, z, 1); mpz_sub(param->nk, param->nk, z); mpz_mul(z, param->r, param->r); mpz_divexact(param->hk, param->nk, z); mpz_clear(z); } field_clear(cc); field_clear(fp); }