static void ep_mul_glv_imp(ep_t r, const ep_t p, const bn_t k) {
	int i, n0, n1, s0, s1;
	int8_t naf0[RLC_FP_BITS + 1], naf1[RLC_FP_BITS + 1], *t0, *t1;
	bn_t n, _k, k0, k1, v1[3], v2[3];
	ep_t q, t[1 << (EP_WIDTH - 2)];
	size_t l, l0, l1;

	bn_null(n);
	bn_null(_k);
	bn_null(k0);
	bn_null(k1);
	ep_null(q);

	RLC_TRY {
		bn_new(n);
		bn_new(_k);
		bn_new(k0);
		bn_new(k1);
		ep_new(q);
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep_null(t[i]);
			ep_new(t[i]);
		}
		for (i = 0; i < 3; i++) {
			bn_null(v1[i]);
			bn_null(v2[i]);
			bn_new(v1[i]);
			bn_new(v2[i]);
		}

		ep_curve_get_ord(n);
		ep_curve_get_v1(v1);
		ep_curve_get_v2(v2);

		bn_mod(_k, k, n);
		bn_rec_glv(k0, k1, _k, n, (const bn_t *)v1, (const bn_t *)v2);
		s0 = bn_sign(k0);
		s1 = bn_sign(k1);
		bn_abs(k0, k0);
		bn_abs(k1, k1);

		if (s0 == RLC_POS) {
			ep_tab(t, p, EP_WIDTH);
		} else {
			ep_neg(q, p);
			ep_tab(t, q, EP_WIDTH);
		}

		l0 = l1 = RLC_FP_BITS + 1;
		bn_rec_naf(naf0, &l0, k0, EP_WIDTH);
		bn_rec_naf(naf1, &l1, k1, EP_WIDTH);

		l = RLC_MAX(l0, l1);
		t0 = naf0 + l - 1;
		t1 = naf1 + l - 1;

		ep_set_infty(r);
		for (i = l - 1; i >= 0; i--, t0--, t1--) {
			ep_dbl(r, r);

			n0 = *t0;
			n1 = *t1;
			if (n0 > 0) {
				ep_add(r, r, t[n0 / 2]);
			}
			if (n0 < 0) {
				ep_sub(r, r, t[-n0 / 2]);
			}
			if (n1 > 0) {
				ep_psi(q, t[n1 / 2]);
				if (s0 != s1) {
					ep_neg(q, q);
				}
				ep_add(r, r, q);
			}
			if (n1 < 0) {
				ep_psi(q, t[-n1 / 2]);
				if (s0 != s1) {
					ep_neg(q, q);
				}
				ep_sub(r, r, q);
			}
		}
		/* Convert r to affine coordinates. */
		ep_norm(r, r);
		if (bn_sign(_k) == RLC_NEG) {
			ep_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(_k);
		bn_free(k0);
		bn_free(k1);
		bn_free(n)
		ep_free(q);
		for (i = 0; i < 1 << (EP_WIDTH - 2); i++) {
			ep_free(t[i]);
		}
		for (i = 0; i < 3; i++) {
			bn_free(v1[i]);
			bn_free(v2[i]);
		}

	}
}


static void ep_mul_reg_imp(ep_t r, const ep_t p, const bn_t k) {
	bn_t _k;
	int i, j, n;
	int8_t s, reg[1 + RLC_CEIL(RLC_FP_BITS + 1, EP_WIDTH - 1)];
	ep_t t[1 << (EP_WIDTH - 2)], u, v;
	size_t l;

	if (bn_is_zero(k)) {
		ep_set_infty(r);
		return;
	}

	bn_null(_k);

	RLC_TRY {
		bn_new(_k);
		ep_new(u);
		ep_new(v);
		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep_null(t[i]);
			ep_new(t[i]);
		}
		/* Compute the precomputation table. */
		ep_tab(t, p, EP_WIDTH);

		ep_curve_get_ord(_k);
		n = bn_bits(_k);

		/* Make a copy of the scalar for processing. */
		bn_abs(_k, k);
		_k->dp[0] |= 1;

		/* Compute the regular w-NAF representation of k. */
		l = RLC_CEIL(n, EP_WIDTH - 1) + 1;
		bn_rec_reg(reg, &l, _k, n, EP_WIDTH);

#if defined(EP_MIXED)
		fp_set_dig(u->z, 1);
		u->coord = BASIC;
#else
		u->coord = EP_ADD;
#endif
		ep_set_infty(r);
		for (i = l - 1; i >= 0; i--) {
			for (j = 0; j < EP_WIDTH - 1; j++) {
				ep_dbl(r, r);
			}

			n = reg[i];
			s = (n >> 7);
			n = ((n ^ s) - s) >> 1;

			for (j = 0; j < (1 << (EP_WIDTH - 2)); j++) {
				dv_copy_cond(u->x, t[j]->x, RLC_FP_DIGS, j == n);
				dv_copy_cond(u->y, t[j]->y, RLC_FP_DIGS, j == n);
#if !defined(EP_MIXED)
				dv_copy_cond(u->z, t[j]->z, RLC_FP_DIGS, j == n);
#endif
			}
			ep_neg(v, u);
			dv_copy_cond(u->y, v->y, RLC_FP_DIGS, s != 0);
			ep_add(r, r, u);
		}
		/* t[0] has an unmodified copy of p. */
		ep_sub(u, r, t[0]);
		dv_copy_cond(r->x, u->x, RLC_FP_DIGS, bn_is_even(k));
		dv_copy_cond(r->y, u->y, RLC_FP_DIGS, bn_is_even(k));
		dv_copy_cond(r->z, u->z, RLC_FP_DIGS, bn_is_even(k));
		/* Convert r to affine coordinates. */
		ep_norm(r, r);
		ep_neg(u, r);
		dv_copy_cond(r->y, u->y, RLC_FP_DIGS, bn_sign(k) == RLC_NEG);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep_free(t[i]);
		}
		bn_free(_k);
		ep_free(u);
		ep_free(v);
	}
}

The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control.


static void ep_mul_fix_plain(ep_t r, const ep_t *t, const bn_t k) {
	int i, n;
	int8_t naf[RLC_FP_BITS + 1];
	size_t l;

	/* Compute the w-TNAF representation of k. */
	l = RLC_FP_BITS + 1;
	bn_rec_naf(naf, &l, k, EP_DEPTH);

	n = naf[l - 1];
	if (n > 0) {
		ep_copy(r, t[n / 2]);
	} else {
		ep_neg(r, t[-n / 2]);
	}

	for (i = l - 2; i >= 0; i--) {
		ep_dbl(r, r);

		n = naf[i];
		if (n > 0) {
			ep_add(r, r, t[n / 2]);
		}
		if (n < 0) {
			ep_sub(r, r, t[-n / 2]);
		}
	}
	/* Convert r to affine coordinates. */
	ep_norm(r, r);
	if (bn_sign(k) == RLC_NEG) {
		ep_neg(r, r);
	}
}


static void ep_mul_sim_plain(ep_t r, const ep_t p, const bn_t k, const ep_t q,
		const bn_t m, const ep_t *t) {
	int i, w, gen;
	int8_t naf0[RLC_FP_BITS + 1], naf1[RLC_FP_BITS + 1], n0, n1, *u, *v;
	ep_t t0[1 << (EP_WIDTH - 2)];
	ep_t t1[1 << (EP_WIDTH - 2)];
	size_t l, l0, l1;

	RLC_TRY {
		gen = (t == NULL ? 0 : 1);
		if (!gen) {
			for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
				ep_null(t0[i]);
				ep_new(t0[i]);
			}
			ep_tab(t0, p, EP_WIDTH);
			t = (const ep_t *)t0;
		}

		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep_null(t1[i]);
			ep_new(t1[i]);
		}

		/* Compute the precomputation table. */
		ep_tab(t1, q, EP_WIDTH);

		/* Compute the w-TNAF representation of k. */
		if (gen) {
			w = EP_DEPTH;
		} else {
			w = EP_WIDTH;
		}
		l0 = l1 = RLC_FP_BITS + 1;
		bn_rec_naf(naf0, &l0, k, w);
		bn_rec_naf(naf1, &l1, m, EP_WIDTH);

		l = RLC_MAX(l0, l1);
		if (bn_sign(k) == RLC_NEG) {
			for (i =  0; i < l0; i++) {
				naf0[i] = -naf0[i];
			}
		}
		if (bn_sign(m) == RLC_NEG) {
			for (i =  0; i < l1; i++) {
				naf1[i] = -naf1[i];
			}
		}

		u = naf0 + l - 1;
		v = naf1 + l - 1;
		ep_set_infty(r);
		for (i = l - 1; i >= 0; i--, u--, v--) {
			ep_dbl(r, r);

			n0 = *u;
			n1 = *v;
			if (n0 > 0) {
				ep_add(r, r, t[n0 / 2]);
			}
			if (n0 < 0) {
				ep_sub(r, r, t[-n0 / 2]);
			}
			if (n1 > 0) {
				ep_add(r, r, t1[n1 / 2]);
			}
			if (n1 < 0) {
				ep_sub(r, r, t1[-n1 / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ep_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation tables. */
		if (!gen) {
			for (i = 0; i < 1 << (EP_WIDTH - 2); i++) {
				ep_free(t0[i]);
			}
		}
		for (i = 0; i < 1 << (EP_WIDTH - 2); i++) {
			ep_free(t1[i]);
		}
	}
}


void ep_mul_sim_joint(ep_t r, const ep_t p, const bn_t k, const ep_t q,
		const bn_t m) {
	bn_t n, _k, _m;
	ep_t t[5];
	int i, u_i, offset;
	int8_t jsf[2 * (RLC_FP_BITS + 1)];
	size_t l;

	if (bn_is_zero(k) || ep_is_infty(p)) {
		ep_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ep_is_infty(q)) {
		ep_mul(r, p, k);
		return;
	}

	bn_null(n);
	bn_null(_k);
	bn_null(_m);

	RLC_TRY {
		bn_new(n);
		bn_new(_k);
		bn_new(_m);
		for (i = 0; i < 5; i++) {
			ep_null(t[i]);
			ep_new(t[i]);
		}

		ep_curve_get_ord(n);
		bn_mod(_k, k, n);
		bn_mod(_m, m, n);

		ep_set_infty(t[0]);
		ep_copy(t[1], q);
		if (bn_sign(_m) == RLC_NEG) {
			ep_neg(t[1], t[1]);
		}
		ep_copy(t[2], p);
		if (bn_sign(_k) == RLC_NEG) {
			ep_neg(t[2], t[2]);
		}
		ep_add(t[3], t[2], t[1]);
		ep_sub(t[4], t[2], t[1]);
#if defined(EP_MIXED)
		ep_norm_sim(t + 3, (const ep_t *)t + 3, 2);
#endif

		l = 2 * (RLC_FP_BITS + 1);
		bn_rec_jsf(jsf, &l, _k, _m);

		ep_set_infty(r);

		offset = RLC_MAX(bn_bits(_k), bn_bits(_m)) + 1;
		for (i = l - 1; i >= 0; i--) {
			ep_dbl(r, r);
			if (jsf[i] != 0 && jsf[i] == -jsf[i + offset]) {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ep_sub(r, r, t[4]);
				} else {
					ep_add(r, r, t[4]);
				}
			} else {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ep_sub(r, r, t[-u_i]);
				} else {
					ep_add(r, r, t[u_i]);
				}
			}
		}
		ep_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(_k);
		bn_free(_m);
		for (i = 0; i < 5; i++) {
			ep_free(t[i]);
		}
	}
}


void ep_mul_sim_trick(ep_t r, const ep_t p, const bn_t k, const ep_t q,
		const bn_t m) {
	ep_t t0[1 << (EP_WIDTH / 2)], t1[1 << (EP_WIDTH / 2)], t[1 << EP_WIDTH];
	bn_t n, _k, _m;
	size_t l0, l1, w = EP_WIDTH / 2;
	uint8_t w0[RLC_FP_BITS + 1], w1[RLC_FP_BITS + 1];

	if (bn_is_zero(k) || ep_is_infty(p)) {
		ep_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ep_is_infty(q)) {
		ep_mul(r, p, k);
		return;
	}

	bn_null(n);
	bn_null(_k);
	bn_null(_m);

	RLC_TRY {
		bn_new(n);
		bn_new(_k);
		bn_new(_m);

		for (int i = 0; i < (1 << w); i++) {
			ep_null(t0[i]);
			ep_null(t1[i]);
			ep_new(t0[i]);
			ep_new(t1[i]);
		}
		for (int i = 0; i < (1 << EP_WIDTH); i++) {
			ep_null(t[i]);
			ep_new(t[i]);
		}

		ep_curve_get_ord(n);
		bn_mod(_k, k, n);
		bn_mod(_m, m, n);

		ep_set_infty(t0[0]);
		ep_copy(t0[1], p);
		if (bn_sign(_k) == RLC_NEG) {
			ep_neg(t0[1], t0[1]);
		}
		for (int i = 2; i < (1 << w); i++) {
			ep_add(t0[i], t0[i - 1], t0[1]);
		}

		ep_set_infty(t1[0]);
		ep_copy(t1[1], q);
		if (bn_sign(_m) == RLC_NEG) {
			ep_neg(t1[1], t1[1]);
		}
		for (int i = 2; i < (1 << w); i++) {
			ep_add(t1[i], t1[i - 1], t1[1]);
		}

		for (int i = 0; i < (1 << w); i++) {
			for (int j = 0; j < (1 << w); j++) {
				ep_add(t[(i << w) + j], t0[i], t1[j]);
			}
		}

#if EP_WIDTH > 2 && defined(EP_MIXED)
		ep_norm_sim(t + 1, (const ep_t *)(t + 1), (1 << EP_WIDTH) - 1);
#endif

		l0 = l1 = RLC_CEIL(RLC_FP_BITS + 1, w);
		bn_rec_win(w0, &l0, _k, w);
		bn_rec_win(w1, &l1, _m, w);
		for (int i = l0; i < l1; i++) {
			w0[i] = 0;
		}
		for (int i = l1; i < l0; i++) {
			w1[i] = 0;
		}

		ep_set_infty(r);
		for (int i = RLC_MAX(l0, l1) - 1; i >= 0; i--) {
			for (int j = 0; j < w; j++) {
				ep_dbl(r, r);
			}
			ep_add(r, r, t[(w0[i] << w) + w1[i]]);
		}
		ep_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(_k);
		bn_free(_m);
		for (int i = 0; i < (1 << w); i++) {
			ep_free(t0[i]);
			ep_free(t1[i]);
		}
		for (int i = 0; i < (1 << EP_WIDTH); i++) {
			ep_free(t[i]);
		}
	}
}


void ep_mul_sim_lot_plain(ep_t r, const ep_t p[], const bn_t k[], int n) {
	int i, j;
	int8_t *naf = NULL;
	ep_t *_p = RLC_ALLOCA(ep_t, n);
	size_t l, *_l = RLC_ALLOCA(size_t, n);

	RLC_TRY {
		l = 0;
		for (i = 0; i < n; i++) {
			l = RLC_MAX(l, bn_bits(k[i]) + 1);
		}
		naf = RLC_ALLOCA(int8_t, n * l);
		if (naf == NULL || _p == NULL || _l == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		for (i = 0; i < n; i++) {
			ep_null(_p[i]);
			ep_new(_p[i]);
		}

		for (i = 0; i < n; i++) {
			_l[i] = l;
			ep_norm(_p[i], p[i]);
			bn_rec_naf(&naf[i*l], &_l[i], k[i], 2);
			if (bn_sign(k[i]) == RLC_NEG) {
				ep_neg(_p[i], _p[i]);
			}
		}

		ep_set_infty(r);
		for (i = l - 1; i >= 0; i--) {
			ep_dbl(r, r);
			for (j = 0; j < n; j++) {
				if (naf[j*l + i] > 0) {
					ep_add(r, r, _p[j]);
				}
				if (naf[j*l + i] < 0) {
					ep_sub(r, r, _p[j]);
				}
			}
		}

		/* Convert r to affine coordinates. */
		ep_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		for (i = 0; i < n; i++) {
			ep_free(_p[i]);
		}
		RLC_FREE(_l);
		RLC_FREE(_p);
		RLC_FREE(naf);
	}
}


void ep_read_bin(ep_t a, const uint8_t *bin, size_t len) {
	if (len == 1) {
		if (bin[0] == 0) {
			ep_set_infty(a);
			return;
		} else {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		}
	}

	if (len != (RLC_FP_BYTES + 1) && len != (2 * RLC_FP_BYTES + 1)) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	a->coord = BASIC;
	fp_set_dig(a->z, 1);
	fp_read_bin(a->x, bin + 1, RLC_FP_BYTES);
	if (len == RLC_FP_BYTES + 1) {
		switch(bin[0]) {
			case 2:
				fp_zero(a->y);
				break;
			case 3:
				fp_zero(a->y);
				fp_set_bit(a->y, 0, 1);
				break;
			default:
				RLC_THROW(ERR_NO_VALID);
				break;
		}
		ep_upk(a, a);
	}

	if (len == 2 * RLC_FP_BYTES + 1) {
		if (bin[0] == 4) {
			fp_read_bin(a->y, bin + RLC_FP_BYTES + 1, RLC_FP_BYTES);
		} else {
			RLC_THROW(ERR_NO_VALID);
			return;
		}
	}

	if (!ep_on_curve(a)) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
}


void ep_write_bin(uint8_t *bin, size_t len, const ep_t a, int pack) {
	ep_t t;

	ep_null(t);

	memset(bin, 0, len);

	if (ep_is_infty(a)) {
		if (len < 1) {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		} else {
			return;
		}
	}

	RLC_TRY {
		ep_new(t);

		ep_norm(t, a);

		if (pack) {
			if (len < RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				ep_pck(t, t);
				bin[0] = 2 | fp_get_bit(t->y, 0);
				fp_write_bin(bin + 1, RLC_FP_BYTES, t->x);
			}
		} else {
			if (len < 2 * RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				bin[0] = 4;
				fp_write_bin(bin + 1, RLC_FP_BYTES, t->x);
				fp_write_bin(bin + RLC_FP_BYTES + 1, RLC_FP_BYTES, t->y);
			}
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ep_free(t);
	}
}


void ep2_map(ep2_t p, const uint8_t *msg, size_t len) {
	ep2_map_dst(p, msg, len, (const uint8_t *)"RELIC", 5);
}


void ep2_map_dst(ep2_t p, const uint8_t *msg, size_t len, const uint8_t *dst,
		size_t dst_len) {
	/* enough space for two field elements plus extra bytes for uniformity */
	const int lpe = (FP_PRIME + ep_param_level() + 7) / 8;
	uint8_t *pseudo_random_bytes = RLC_ALLOCA(uint8_t, 4 * lpe);

	RLC_TRY {
		/* XXX(rsw) See note in ep/relic_ep_map.c about using MD_MAP. */
		/* hash to a pseudorandom string using md_xmd */
		md_xmd(pseudo_random_bytes, 4 * lpe, msg, len, dst, dst_len);
		ep2_map_from_field(p, pseudo_random_bytes, 2 * lpe);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		RLC_FREE(pseudo_random_bytes);
	}
}


void ep2_mul_dig(ep2_t r, const ep2_t p, const dig_t k) {
	ep2_t t;
	bn_t _k;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	ep2_null(t);
	bn_null(_k);

	if (k == 0 || ep2_is_infty(p)) {
		ep2_set_infty(r);
		return;
	}

	RLC_TRY {
		ep2_new(t);
		bn_new(_k);

		bn_set_dig(_k, k);

		l = RLC_DIG + 1;
		bn_rec_naf(naf, &l, _k, 2);

		ep2_set_infty(t);
		for (int i = l - 1; i >= 0; i--) {
			ep2_dbl(t, t);

			u = naf[i];
			if (u > 0) {
				ep2_add(t, t, p);
			} else if (u < 0) {
				ep2_sub(t, t, p);
			}
		}

		ep2_norm(r, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ep2_free(t);
		bn_free(_k);
	}
}


void ep2_mul_basic(ep2_t r, const ep2_t p, const bn_t k) {
	size_t l;
	ep2_t t;

	ep2_null(t);

	if (bn_is_zero(k) || ep2_is_infty(p)) {
		ep2_set_infty(r);
		return;
	}

	RLC_TRY {
		ep2_new(t);
		l = bn_bits(k);

		if (bn_get_bit(k, l - 1)) {
			ep2_copy(t, p);
		} else {
			ep2_set_infty(t);
		}

		for (int i = l - 2; i >= 0; i--) {
			ep2_dbl(t, t);
			if (bn_get_bit(k, i)) {
				ep2_add(t, t, p);
			}
		}

		ep2_copy(r, t);
		ep2_norm(r, r);
		if (bn_sign(k) == RLC_NEG) {
			ep2_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ep2_free(t);
	}
}


void ep2_mul_slide(ep2_t r, const ep2_t p, const bn_t k) {
	ep2_t t[1 << (EP_WIDTH - 1)], q;
	uint8_t win[RLC_FP_BITS + 1];
	size_t l;

	ep2_null(q);

	if (bn_is_zero(k) || ep2_is_infty(p)) {
		ep2_set_infty(r);
		return;
	}

	RLC_TRY {
		for (int i = 0; i < (1 << (EP_WIDTH - 1)); i ++) {
			ep2_null(t[i]);
			ep2_new(t[i]);
		}

		ep2_new(q);

		ep2_copy(t[0], p);
		ep2_dbl(q, p);

#if defined(EP_MIXED)
		ep2_norm(q, q);
#endif

		/* Create table. */
		for (size_t i = 1; i < (1 << (EP_WIDTH - 1)); i++) {
			ep2_add(t[i], t[i - 1], q);
		}

#if defined(EP_MIXED)
		ep2_norm_sim(t + 1, t + 1, (1 << (EP_WIDTH - 1)) - 1);
#endif

		ep2_set_infty(q);
		l = RLC_FP_BITS + 1;
		bn_rec_slw(win, &l, k, EP_WIDTH);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				ep2_dbl(q, q);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					ep2_dbl(q, q);
				}
				ep2_add(q, q, t[win[i] >> 1]);
			}
		}

		ep2_norm(r, q);
		if (bn_sign(k) == RLC_NEG) {
			ep2_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i++) {
			ep2_free(t[i]);
		}
		ep2_free(q);
	}
}


static void ep2_mul_naf_imp(ep2_t r, const ep2_t p, const bn_t k) {
	size_t l, n;
	int8_t naf[RLC_FP_BITS + 1];
	ep2_t t[1 << (EP_WIDTH - 2)];

	RLC_TRY {
		/* Prepare the precomputation table. */
		for (int i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep2_null(t[i]);
			ep2_new(t[i]);
		}
		/* Compute the precomputation table. */
		ep2_tab(t, p, EP_WIDTH);

		/* Compute the w-NAF representation of k. */
		l = sizeof(naf);
		bn_rec_naf(naf, &l, k, EP_WIDTH);

		ep2_set_infty(r);
		for (int i = l - 1; i >= 0; i--) {
			ep2_dbl(r, r);

			n = naf[i];
			if (n > 0) {
				ep2_add(r, r, t[n / 2]);
			}
			if (n < 0) {
				ep2_sub(r, r, t[-n / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ep2_norm(r, r);
		if (bn_sign(k) == RLC_NEG) {
			ep2_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (int i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep2_free(t[i]);
		}
	}
}


static void ep2_mul_glv_imp(ep2_t r, const ep2_t p, const bn_t k) {
	size_t l, _l[4];
	bn_t n, _k[4], u;
	int8_t naf[4][RLC_FP_BITS + 1];
	ep2_t q[4];

	bn_null(n);
	bn_null(u);

	RLC_TRY {
		bn_new(n);
		bn_new(u);
		for (int i = 0; i < 4; i++) {
			bn_null(_k[i]);
			ep2_null(q[i]);
			bn_new(_k[i]);
			ep2_new(q[i]);
		}

		ep2_curve_get_ord(n);
		fp_prime_get_par(u);
		bn_mod(_k[0], k, n);
		bn_rec_frb(_k, 4, _k[0], u, n, ep_curve_is_pairf() == EP_BN);

		ep2_norm(q[0], p);
		ep2_frb(q[1], q[0], 1);
		ep2_frb(q[2], q[1], 1);
		ep2_frb(q[3], q[2], 1);

		l = 0;
		for (int i = 0; i < 4; i++) {
			if (bn_sign(_k[i]) == RLC_NEG) {
				ep2_neg(q[i], q[i]);
			}
			_l[i] = RLC_FP_BITS + 1;
			bn_rec_naf(naf[i], &_l[i], _k[i], 2);
			l = RLC_MAX(l, _l[i]);
		}

		ep2_set_infty(r);
		for (int j = l - 1; j >= 0; j--) {
			ep2_dbl(r, r);

			for (int i = 0; i < 4; i++) {
				if (naf[i][j] > 0) {
					ep2_add(r, r, q[i]);
				}
				if (naf[i][j] < 0) {
					ep2_sub(r, r, q[i]);
				}
			}
		}

		/* Convert r to affine coordinates. */
		ep2_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(u);
		for (int i = 0; i < 4; i++) {
			bn_free(_k[i]);
			ep2_free(q[i]);
		}

	}
}


static void ep2_mul_fix_plain(ep2_t r, const ep2_t *table, const bn_t k) {
	int8_t naf[2 * RLC_FP_BITS + 1], *t;
	size_t len;
	int n;

	if (bn_is_zero(k)) {
		ep2_set_infty(r);
		return;
	}

	/* Compute the w-TNAF representation of k. */
	len = 2 * RLC_FP_BITS + 1;
	bn_rec_naf(naf, &len, k, EP_DEPTH);

	t = naf + len - 1;
	ep2_set_infty(r);
	for (int i = len - 1; i >= 0; i--, t--) {
		ep2_dbl(r, r);

		n = *t;
		if (n > 0) {
			ep2_add(r, r, table[n / 2]);
		}
		if (n < 0) {
			ep2_sub(r, r, table[-n / 2]);
		}
	}
	/* Convert r to affine coordinates. */
	ep2_norm(r, r);
	if (bn_sign(k) == RLC_NEG) {
		ep2_neg(r, r);
	}
}


void ep2_mul_sim_dig(ep2_t r, const ep2_t p[], const dig_t k[], size_t len) {
	ep2_t t;
	int max;

	ep2_null(t);

	max = util_bits_dig(k[0]);
	for (int i = 1; i < len; i++) {
		max = RLC_MAX(max, util_bits_dig(k[i]));
	}

	RLC_TRY {
		ep2_new(t);

		ep2_set_infty(t);
		for (int i = max - 1; i >= 0; i--) {
			ep2_dbl(t, t);
			for (int j = 0; j < len; j++) {
				if (k[j] & ((dig_t)1 << i)) {
					ep2_add(t, t, p[j]);
				}
			}
		}

		ep2_norm(r, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ep2_free(t);
	}
}


static void ep2_mul_sim_plain(ep2_t r, const ep2_t p, const bn_t k,
		const ep2_t q, const bn_t m, const ep2_t *t) {
	int i, n0, n1, w, gen;
	int8_t naf0[2 * RLC_FP_BITS + 1], naf1[2 * RLC_FP_BITS + 1], *_k, *_m;
	ep2_t t0[1 << (EP_WIDTH - 2)];
	ep2_t t1[1 << (EP_WIDTH - 2)];
	size_t l, l0, l1;

	RLC_TRY {
		gen = (t == NULL ? 0 : 1);
		if (!gen) {
			for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
				ep2_null(t0[i]);
				ep2_new(t0[i]);
			}
			ep2_tab(t0, p, EP_WIDTH);
			t = (ep2_t *)t0;
		}

		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep2_null(t1[i]);
			ep2_new(t1[i]);
		}
		/* Compute the precomputation table. */
		ep2_tab(t1, q, EP_WIDTH);

		/* Compute the w-TNAF representation of k. */
		if (gen) {
			w = EP_DEPTH;
		} else {
			w = EP_WIDTH;
		}
		l0 = l1 = 2 * RLC_FP_BITS + 1;
		bn_rec_naf(naf0, &l0, k, w);
		bn_rec_naf(naf1, &l1, m, EP_WIDTH);

		l = RLC_MAX(l0, l1);
		_k = naf0 + l - 1;
		_m = naf1 + l - 1;
		if (bn_sign(k) == RLC_NEG) {
			for (i =  0; i < l0; i++) {
				naf0[i] = -naf0[i];
			}
		}
		if (bn_sign(m) == RLC_NEG) {
			for (i =  0; i < l1; i++) {
				naf1[i] = -naf1[i];
			}
		}

		ep2_set_infty(r);
		for (i = l - 1; i >= 0; i--, _k--, _m--) {
			ep2_dbl(r, r);

			n0 = *_k;
			n1 = *_m;
			if (n0 > 0) {
				ep2_add(r, r, t[n0 / 2]);
			}
			if (n0 < 0) {
				ep2_sub(r, r, t[-n0 / 2]);
			}
			if (n1 > 0) {
				ep2_add(r, r, t1[n1 / 2]);
			}
			if (n1 < 0) {
				ep2_sub(r, r, t1[-n1 / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ep2_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation tables. */
		if (!gen) {
			for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
				ep2_free(t0[i]);
			}
		}
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep2_free(t1[i]);
		}
	}
}


static void ep2_mul_sim_endom(ep2_t r, const ep2_t p, const bn_t k, ep2_t q,
		const bn_t m) {
	size_t l, _l[4];
	bn_t _k[4], _m[4], n, u;
	int8_t naf0[4][RLC_FP_BITS + 1];
	int8_t naf1[4][RLC_FP_BITS + 1];
	ep2_t _p[4], _q[4];

	bn_null(n);
	bn_null(u);

	RLC_TRY {
		bn_new(n);
		bn_new(u);
		for (int i = 0; i < 4; i++) {
			bn_null(_k[i]);
			bn_new(_k[i]);
			bn_null(_m[i]);
			bn_new(_m[i]);
			ep2_null(_p[i]);
			ep2_null(_q[i]);
			ep2_new(_p[i]);
			ep2_new(_q[i]);
		}

		ep2_norm(_p[0], p);
		ep2_frb(_p[1], _p[0], 1);
		ep2_frb(_p[2], _p[1], 1);
		ep2_frb(_p[3], _p[2], 1);
		ep2_norm(_q[0], q);
		ep2_frb(_q[1], _q[0], 1);
		ep2_frb(_q[2], _q[1], 1);
		ep2_frb(_q[3], _q[2], 1);

		ep2_curve_get_ord(n);
		fp_prime_get_par(u);
		bn_mod(_k[0], k, n);
		bn_rec_frb(_k, 4, _k[0], u, n, ep_curve_is_pairf() == EP_BN);
		bn_mod(_m[0], m, n);
		bn_rec_frb(_m, 4, _m[0], u, n, ep_curve_is_pairf() == EP_BN);

		l = 0;
		for (int i = 0; i < 4; i++) {
			_l[i] = RLC_FP_BITS + 1;
			bn_rec_naf(naf0[i], &_l[i], _k[i], 2);
			if (bn_sign(_k[i]) == RLC_NEG) {
				ep2_neg(_p[i], _p[i]);
			}
			l = RLC_MAX(l, _l[i]);
			_l[i] = RLC_FP_BITS + 1;
			bn_rec_naf(naf1[i], &_l[i], _m[i], 2);
			if (bn_sign(_m[i]) == RLC_NEG) {
				ep2_neg(_q[i], _q[i]);
			}
			l = RLC_MAX(l, _l[i]);
		}

		ep2_set_infty(r);
		for (int i = l - 1; i >= 0; i--) {
			ep2_dbl(r, r);
			for (int j = 0; j < 4; j++) {
				if (naf0[j][i] > 0) {
					ep2_add(r, r, _p[j]);
				}
				if (naf0[j][i] < 0) {
					ep2_sub(r, r, _p[j]);
				}
				if (naf1[j][i] > 0) {
					ep2_add(r, r, _q[j]);
				}
				if (naf1[j][i] < 0) {
					ep2_sub(r, r, _q[j]);
				}
			}
		}

		/* Convert r to affine coordinates. */
		ep2_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		bn_free(n);
		bn_free(u);
		for (int i = 0; i < 4; i++) {
			bn_free(_k[i]);
			bn_free(_m[i]);
			ep2_free(_p[i]);
			ep2_free(_q[i]);
		}
	}
}


void ep2_mul_sim_joint(ep2_t r, const ep2_t p, const bn_t k, const ep2_t q,
		const bn_t m) {
	bn_t n, _k, _m;
	ep2_t t[5];
	int i, u_i, offset;
	int8_t jsf[2 * (RLC_FP_BITS + 1)];
	size_t l;

	if (bn_is_zero(k) || ep2_is_infty(p)) {
		ep2_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ep2_is_infty(q)) {
		ep2_mul(r, p, k);
		return;
	}

	bn_null(n);
	bn_null(_k);
	bn_null(_m);

	RLC_TRY {
		bn_new(n);
		bn_new(_k);
		bn_new(_m);
		for (i = 0; i < 5; i++) {
			ep2_null(t[i]);
			ep2_new(t[i]);
		}

		ep2_curve_get_ord(n);
		bn_mod(_k, k, n);
		bn_mod(_m, m, n);

		ep2_set_infty(t[0]);
		ep2_copy(t[1], q);
		if (bn_sign(_m) == RLC_NEG) {
			ep2_neg(t[1], t[1]);
		}
		ep2_copy(t[2], p);
		if (bn_sign(_k) == RLC_NEG) {
			ep2_neg(t[2], t[2]);
		}
		ep2_add(t[3], t[2], t[1]);
		ep2_sub(t[4], t[2], t[1]);
#if defined(EP_MIXED)
		ep2_norm_sim(t + 3, t + 3, 2);
#endif

		l = 2 * (RLC_FP_BITS + 1);
		bn_rec_jsf(jsf, &l, _k, _m);

		ep2_set_infty(r);

		offset = RLC_MAX(bn_bits(_k), bn_bits(_m)) + 1;
		for (i = l - 1; i >= 0; i--) {
			ep2_dbl(r, r);
			if (jsf[i] != 0 && jsf[i] == -jsf[i + offset]) {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ep2_sub(r, r, t[4]);
				} else {
					ep2_add(r, r, t[4]);
				}
			} else {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ep2_sub(r, r, t[-u_i]);
				} else {
					ep2_add(r, r, t[u_i]);
				}
			}
		}
		ep2_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		bn_free(_k);
		bn_free(_m);
		for (i = 0; i < 5; i++) {
			ep2_free(t[i]);
		}
	}
}


void ep2_write_bin(uint8_t *bin, size_t len, const ep2_t a, int pack) {
	ep2_t t;

	ep2_null(t);

	memset(bin, 0, len);

	if (ep2_is_infty(a)) {
		if (len < 1) {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		} else {
			return;
		}
	}

	RLC_TRY {
		ep2_new(t);

		ep2_norm(t, a);

		if (pack) {
			if (len < 2 * RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				ep2_pck(t, t);
				bin[0] = 2 | fp_get_bit(t->y[0], 0);
				fp2_write_bin(bin + 1, 2 * RLC_FP_BYTES, t->x, 0);
			}
		} else {
			if (len < 4 * RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
			} else {
				bin[0] = 4;
				fp2_write_bin(bin + 1, 2 * RLC_FP_BYTES, t->x, 0);
				fp2_write_bin(bin + 2 * RLC_FP_BYTES + 1, 2 * RLC_FP_BYTES, t->y, 0);
			}
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		ep2_free(t);
	}
}


void ep2_read_bin(ep2_t a, const uint8_t *bin, size_t len) {
	if (len == 1) {
		if (bin[0] == 0) {
			ep2_set_infty(a);
			return;
		} else {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		}
	}

	if (len != (2 * RLC_FP_BYTES + 1) && len != (4 * RLC_FP_BYTES + 1)) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	a->coord = BASIC;
	fp2_set_dig(a->z, 1);
	fp2_read_bin(a->x, bin + 1, 2 * RLC_FP_BYTES);
	if (len == 2 * RLC_FP_BYTES + 1) {
		switch(bin[0]) {
			case 2:
				fp2_zero(a->y);
				break;
			case 3:
				fp2_zero(a->y);
				fp_set_bit(a->y[0], 0, 1);
				fp_zero(a->y[1]);
				break;
			default:
				RLC_THROW(ERR_NO_VALID);
				break;
		}
		ep2_upk(a, a);
	}

	if (len == 4 * RLC_FP_BYTES + 1) {
		if (bin[0] == 4) {
			fp2_read_bin(a->y, bin + 2 * RLC_FP_BYTES + 1, 2 * RLC_FP_BYTES);
		} else {
			RLC_THROW(ERR_NO_VALID);
			return;
		}
	}

	if (!ep2_on_curve(a)) {
		RLC_THROW(ERR_NO_VALID);
	}
}


void ep4_map(ep4_t p, const uint8_t *msg, size_t len) {
	bn_t x;
	fp4_t t0;
	uint8_t digest[RLC_MD_LEN];

	bn_null(x);
	fp4_null(t0);

	RLC_TRY {
		bn_new(x);
		fp4_new(t0);

		md_map(digest, msg, len);
		bn_read_bin(x, digest, RLC_MIN(RLC_FP_BYTES, RLC_MD_LEN));

		fp4_zero(p->x);
		fp_prime_conv(p->x[0][0], x);
		fp4_set_dig(p->z, 1);

		while (1) {
			ep4_rhs(t0, p);

			if (fp4_srt(p->y, t0)) {
				p->coord = BASIC;
				break;
			}

			fp_add_dig(p->x[0][0], p->x[0][0], 1);
		}

		ep4_mul_cof(p, p);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(x);
		fp4_free(t0);
	}
}


static void ep4_mul_naf_imp(ep4_t r, const ep4_t p, const bn_t k) {
	int i, n;
	int8_t naf[RLC_FP_BITS + 1];
	ep4_t t[1 << (EP_WIDTH - 2)];
	size_t l;

	RLC_TRY {
		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep4_null(t[i]);
			ep4_new(t[i]);
		}
		/* Compute the precomputation table. */
		ep4_tab(t, p, EP_WIDTH);

		/* Compute the w-NAF representation of k. */
		l = sizeof(naf);
		bn_rec_naf(naf, &l, k, EP_WIDTH);

		ep4_set_infty(r);
		for (i = l - 1; i >= 0; i--) {
			ep4_dbl(r, r);

			n = naf[i];
			if (n > 0) {
				ep4_add(r, r, t[n / 2]);
			}
			if (n < 0) {
				ep4_sub(r, r, t[-n / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ep4_norm(r, r);
		if (bn_sign(k) == RLC_NEG) {
			ep4_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep4_free(t[i]);
		}
	}
}


static void ep4_mul_glv_imp(ep4_t r, const ep4_t p, const bn_t k) {
	int sign, i, j;
	bn_t n, _k[8], u, v;
	int8_t naf[8][RLC_FP_BITS + 1];
	ep4_t q[8];
	size_t l, _l[8];

	bn_null(n);
	bn_null(u);
	bn_null(v);

	RLC_TRY {
		bn_new(n);
		bn_new(u);
		bn_new(v);
		for (i = 0; i < 8; i++) {
			bn_null(_k[i]);
			ep4_null(q[i]);
			bn_new(_k[i]);
			ep4_new(q[i]);
		}

        bn_abs(v, k);
		ep4_curve_get_ord(n);
        if (bn_cmp_abs(v, n) == RLC_GT) {
            bn_mod(v, v, n);
        }

		fp_prime_get_par(u);
		sign = bn_sign(u);
        bn_abs(u, u);

		ep4_norm(q[0], p);
		for (i = 0; i < 8; i++) {
			bn_mod(_k[i], v, u);
			bn_div(v, v, u);
			if ((sign == RLC_NEG) && (i % 2 != 0)) {
				bn_neg(_k[i], _k[i]);
			}
            if (bn_sign(k) == RLC_NEG) {
                bn_neg(_k[i], _k[i]);
            }
            if (i > 0) {
                ep4_frb(q[i], q[i - 1], 1);
            }
		}

        l = 0;
		for (i = 0; i < 8; i++) {
			if (bn_sign(_k[i]) == RLC_NEG) {
				ep4_neg(q[i], q[i]);
			}
			_l[i] = RLC_FP_BITS + 1;
			bn_rec_naf(naf[i], &_l[i], _k[i], 2);
            l = RLC_MAX(l, _l[i]);
		}

		ep4_set_infty(r);
		for (j = l - 1; j >= 0; j--) {
			ep4_dbl(r, r);

			for (i = 0; i < 8; i++) {
				if (naf[i][j] > 0) {
					ep4_add(r, r, q[i]);
				}
				if (naf[i][j] < 0) {
					ep4_sub(r, r, q[i]);
				}
			}
		}

		/* Convert r to affine coordinates. */
		ep4_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
        bn_free(u);
        bn_free(v);
		for (i = 0; i < 8; i++) {
			bn_free(_k[i]);
			ep4_free(q[i]);
		}

	}
}


void ep4_mul_slide(ep4_t r, const ep4_t p, const bn_t k) {
	ep4_t t[1 << (EP_WIDTH - 1)], q;
	uint8_t win[RLC_FP_BITS + 1];
	size_t l;

	ep4_null(q);

	if (bn_is_zero(k) || ep4_is_infty(p)) {
		ep4_set_infty(r);
		return;
	}

	RLC_TRY {
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i ++) {
			ep4_null(t[i]);
			ep4_new(t[i]);
		}

		ep4_new(q);

		ep4_copy(t[0], p);
		ep4_dbl(q, p);

#if defined(EP_MIXED)
		ep4_norm(q, q);
#endif

		/* Create table. */
		for (size_t i = 1; i < (1 << (EP_WIDTH - 1)); i++) {
			ep4_add(t[i], t[i - 1], q);
		}

#if defined(EP_MIXED)
		ep4_norm_sim(t + 1, t + 1, (1 << (EP_WIDTH - 1)) - 1);
#endif

		ep4_set_infty(q);
		l = RLC_FP_BITS + 1;
		bn_rec_slw(win, &l, k, EP_WIDTH);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				ep4_dbl(q, q);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					ep4_dbl(q, q);
				}
				ep4_add(q, q, t[win[i] >> 1]);
			}
		}

		ep4_norm(r, q);
		if (bn_sign(k) == RLC_NEG) {
			ep4_neg(r, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (size_t i = 0; i < (1 << (EP_WIDTH - 1)); i++) {
			ep4_free(t[i]);
		}
		ep4_free(q);
	}
}


static void ep4_mul_pre_ordin(ep4_t *t, const ep4_t p) {
	ep4_dbl(t[0], p);
#if defined(EP_MIXED)
	ep4_norm(t[0], t[0]);
#endif

#if EP_DEPTH > 2
	ep4_add(t[1], t[0], p);
	for (int i = 2; i < (1 << (EP_DEPTH - 2)); i++) {
		ep4_add(t[i], t[i - 1], t[0]);
	}

#if defined(EP_MIXED)
	for (int i = 1; i < (1 << (EP_DEPTH - 2)); i++) {
		ep4_norm(t[i], t[i]);
	}
#endif

#endif
	ep4_copy(t[0], p);
}


static void ep4_mul_fix_ordin(ep4_t r, const ep4_t *table, const bn_t k) {
	int8_t naf[2 * RLC_FP_BITS + 1], *t;
	size_t len;
	int n;

	if (bn_is_zero(k)) {
		ep4_set_infty(r);
		return;
	}

	/* Compute the w-TNAF representation of k. */
	len = 2 * RLC_FP_BITS + 1;
	bn_rec_naf(naf, &len, k, EP_DEPTH);

	t = naf + len - 1;
	ep4_set_infty(r);
	for (int i = len - 1; i >= 0; i--, t--) {
		ep4_dbl(r, r);

		n = *t;
		if (n > 0) {
			ep4_add(r, r, table[n / 2]);
		}
		if (n < 0) {
			ep4_sub(r, r, table[-n / 2]);
		}
	}
	/* Convert r to affine coordinates. */
	ep4_norm(r, r);
	if (bn_sign(k) == RLC_NEG) {
		ep4_neg(r, r);
	}
}


void ep4_mul_sim_trick(ep4_t r, const ep4_t p, const bn_t k, const ep4_t q,
		const bn_t m) {
	ep4_t t0[1 << (EP_WIDTH / 2)];
	ep4_t t1[1 << (EP_WIDTH / 2)];
	ep4_t t[1 << EP_WIDTH];
	bn_t n;
	size_t l0, l1, w = EP_WIDTH / 2;
	uint8_t w0[2 * RLC_FP_BITS], w1[2 * RLC_FP_BITS];

	bn_null(n);

	if (bn_is_zero(k) || ep4_is_infty(p)) {
		ep4_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ep4_is_infty(q)) {
		ep4_mul(r, p, k);
		return;
	}

	RLC_TRY {
		bn_new(n);

		ep4_curve_get_ord(n);

		for (int i = 0; i < (1 << w); i++) {
			ep4_null(t0[i]);
			ep4_null(t1[i]);
			ep4_new(t0[i]);
			ep4_new(t1[i]);
		}
		for (int i = 0; i < (1 << EP_WIDTH); i++) {
			ep4_null(t[i]);
			ep4_new(t[i]);
		}

		ep4_set_infty(t0[0]);
		ep4_copy(t0[1], p);
		if (bn_sign(k) == RLC_NEG) {
			ep4_neg(t0[1], t0[1]);
		}
		for (int i = 2; i < (1 << w); i++) {
			ep4_add(t0[i], t0[i - 1], t0[1]);
		}

		ep4_set_infty(t1[0]);
		ep4_copy(t1[1], q);
		if (bn_sign(m) == RLC_NEG) {
			ep4_neg(t1[1], t1[1]);
		}
		for (int i = 1; i < (1 << w); i++) {
			ep4_add(t1[i], t1[i - 1], t1[1]);
		}

		for (int i = 0; i < (1 << w); i++) {
			for (int j = 0; j < (1 << w); j++) {
				ep4_add(t[(i << w) + j], t0[i], t1[j]);
			}
		}

#if defined(EP_MIXED)
		ep4_norm_sim(t + 1, t + 1, (1 << (EP_WIDTH)) - 1);
#endif

		l0 = l1 = RLC_CEIL(2 * RLC_FP_BITS, w);
		bn_rec_win(w0, &l0, k, w);
		bn_rec_win(w1, &l1, m, w);

		for (int i = l0; i < l1; i++) {
			w0[i] = 0;
		}
		for (int i = l1; i < l0; i++) {
			w1[i] = 0;
		}

		ep4_set_infty(r);
		for (int i = RLC_MAX(l0, l1) - 1; i >= 0; i--) {
			for (int j = 0; j < w; j++) {
				ep4_dbl(r, r);
			}
			ep4_add(r, r, t[(w0[i] << w) + w1[i]]);
		}
		ep4_norm(r, r);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(n);
		for (int i = 0; i < (1 << w); i++) {
			ep4_free(t0[i]);
			ep4_free(t1[i]);
		}
		for (int i = 0; i < (1 << EP_WIDTH); i++) {
			ep4_free(t[i]);
		}
	}
}


void ep4_mul_sim_dig(ep4_t r, const ep4_t p[], const dig_t k[], size_t len) {
	ep4_t t;
	int max;

	ep4_null(t);

	max = util_bits_dig(k[0]);
	for (int i = 1; i < len; i++) {
		max = RLC_MAX(max, util_bits_dig(k[i]));
	}

	RLC_TRY {
		ep4_new(t);

		ep4_set_infty(t);
		for (int i = max - 1; i >= 0; i--) {
			ep4_dbl(t, t);
			for (int j = 0; j < len; j++) {
				if (k[j] & ((dig_t)1 << i)) {
					ep4_add(t, t, p[j]);
				}
			}
		}

		ep4_norm(r, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		ep4_free(t);
	}
}


void ep4_mul_sim_joint(ep4_t r, const ep4_t p, const bn_t k, const ep4_t q,
		const bn_t m) {
	ep4_t t[5];
	int i, u_i, offset;
	int8_t jsf[4 * (RLC_FP_BITS + 1)];
	size_t l;

	if (bn_is_zero(k) || ep4_is_infty(p)) {
		ep4_mul(r, q, m);
		return;
	}
	if (bn_is_zero(m) || ep4_is_infty(q)) {
		ep4_mul(r, p, k);
		return;
	}

	RLC_TRY {
		for (i = 0; i < 5; i++) {
			ep4_null(t[i]);
			ep4_new(t[i]);
		}

		ep4_set_infty(t[0]);
		ep4_copy(t[1], q);
		if (bn_sign(m) == RLC_NEG) {
			ep4_neg(t[1], t[1]);
		}
		ep4_copy(t[2], p);
		if (bn_sign(k) == RLC_NEG) {
			ep4_neg(t[2], t[2]);
		}
		ep4_add(t[3], t[2], t[1]);
		ep4_sub(t[4], t[2], t[1]);
#if defined(EP_MIXED)
		ep4_norm_sim(t + 3, t + 3, 2);
#endif

		l = 4 * (RLC_FP_BITS + 1);
		bn_rec_jsf(jsf, &l, k, m);

		ep4_set_infty(r);

		offset = RLC_MAX(bn_bits(k), bn_bits(m)) + 1;
		for (i = l - 1; i >= 0; i--) {
			ep4_dbl(r, r);
			if (jsf[i] != 0 && jsf[i] == -jsf[i + offset]) {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ep4_sub(r, r, t[4]);
				} else {
					ep4_add(r, r, t[4]);
				}
			} else {
				u_i = jsf[i] * 2 + jsf[i + offset];
				if (u_i < 0) {
					ep4_sub(r, r, t[-u_i]);
				} else {
					ep4_add(r, r, t[u_i]);
				}
			}
		}
		ep4_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i = 0; i < 5; i++) {
			ep4_free(t[i]);
		}
	}
}


static void ep4_mul_sim_plain(ep4_t r, const ep4_t p, const bn_t k,
		const ep4_t q, const bn_t m, ep4_t *t) {
	int i, n0, n1, w, gen;
	int8_t naf0[2 * RLC_FP_BITS + 1], naf1[2 * RLC_FP_BITS + 1], *_k, *_m;
	ep4_t t0[1 << (EP_WIDTH - 2)];
	ep4_t t1[1 << (EP_WIDTH - 2)];
	size_t l, l0, l1;

	RLC_TRY {
		gen = (t == NULL ? 0 : 1);
		if (!gen) {
			for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
				ep4_null(t0[i]);
				ep4_new(t0[i]);
			}
			ep4_tab(t0, p, EP_WIDTH);
			t = (ep4_t *)t0;
		}

		/* Prepare the precomputation table. */
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep4_null(t1[i]);
			ep4_new(t1[i]);
		}
		/* Compute the precomputation table. */
		ep4_tab(t1, q, EP_WIDTH);

		/* Compute the w-TNAF representation of k. */
		if (gen) {
			w = EP_DEPTH;
		} else {
			w = EP_WIDTH;
		}
		l0 = l1 = 2 * RLC_FP_BITS + 1;
		bn_rec_naf(naf0, &l0, k, w);
		bn_rec_naf(naf1, &l1, m, EP_WIDTH);

		l = RLC_MAX(l0, l1);
		_k = naf0 + l - 1;
		_m = naf1 + l - 1;
		if (bn_sign(k) == RLC_NEG) {
			for (i =  0; i < l0; i++) {
				naf0[i] = -naf0[i];
			}
		}
		if (bn_sign(m) == RLC_NEG) {
			for (i =  0; i < l1; i++) {
				naf1[i] = -naf1[i];
			}
		}

		ep4_set_infty(r);
		for (i = l - 1; i >= 0; i--, _k--, _m--) {
			ep4_dbl(r, r);

			n0 = *_k;
			n1 = *_m;
			if (n0 > 0) {
				ep4_add(r, r, t[n0 / 2]);
			}
			if (n0 < 0) {
				ep4_sub(r, r, t[-n0 / 2]);
			}
			if (n1 > 0) {
				ep4_add(r, r, t1[n1 / 2]);
			}
			if (n1 < 0) {
				ep4_sub(r, r, t1[-n1 / 2]);
			}
		}
		/* Convert r to affine coordinates. */
		ep4_norm(r, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		/* Free the precomputation tables. */
		if (!gen) {
			for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
				ep4_free(t0[i]);
			}
		}
		for (i = 0; i < (1 << (EP_WIDTH - 2)); i++) {
			ep4_free(t1[i]);
		}
	}
}


void ep4_read_bin(ep4_t a, const uint8_t *bin, size_t len) {
	if (len == 1) {
		if (bin[0] == 0) {
			ep4_set_infty(a);
			return;
		} else {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		}
	}

	if (len != (8 * RLC_FP_BYTES + 1)) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	a->coord = BASIC;
	fp4_set_dig(a->z, 1);
	fp4_read_bin(a->x, bin + 1, 4 * RLC_FP_BYTES);

	if (len == 8 * RLC_FP_BYTES + 1) {
		if (bin[0] == 4) {
			fp4_read_bin(a->y, bin + 4 * RLC_FP_BYTES + 1, 4 * RLC_FP_BYTES);
		} else {
			RLC_THROW(ERR_NO_VALID);
			return;
		}
	}

	if (!ep4_on_curve(a)) {
		RLC_THROW(ERR_NO_VALID);
	}
}


void ep4_write_bin(uint8_t *bin, size_t len, const ep4_t a, int pack) {
	ep4_t t;

	ep4_null(t);

	memset(bin, 0, len);

	if (ep4_is_infty(a)) {
		if (len < 1) {
			RLC_THROW(ERR_NO_BUFFER);
			return;
		} else {
			return;
		}
	}

	RLC_TRY {
		ep4_new(t);

		ep4_norm(t, a);

		if (len < 8 * RLC_FP_BYTES + 1) {
			RLC_THROW(ERR_NO_BUFFER);
		} else {
			bin[0] = 4;
			fp4_write_bin(bin + 1, 4 * RLC_FP_BYTES, t->x);
			fp4_write_bin(bin + 4 * RLC_FP_BYTES + 1, 4 * RLC_FP_BYTES, t->y);
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		ep4_free(t);
	}
}


void fb_exp_basic(fb_t c, const fb_t a, const bn_t b) {
	size_t l;
	fb_t r;

	if (bn_is_zero(b)) {
		fb_set_dig(c, 1);
		return;
	}

	fb_null(r);

	RLC_TRY {
		fb_new(r);

		l = bn_bits(b);

		fb_copy(r, a);

		for (int i = l - 2; i >= 0; i--) {
			fb_sqr(r, r);
			if (bn_get_bit(b, i)) {
				fb_mul(r, r, a);
			}
		}

		if (bn_sign(b) == RLC_NEG) {
			fb_inv(c, r);
		} else {
			fb_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fb_free(r);
	}
}


void fb_exp_slide(fb_t c, const fb_t a, const bn_t b) {
	fb_t t[1 << (FB_WIDTH - 1)], r;
	uint8_t win[RLC_FB_BITS + 1];
	size_t l;

	fb_null(r);

	if (bn_is_zero(b)) {
		fb_set_dig(c, 1);
		return;
	}

	/* Initialize table. */
	for (size_t i = 0; i < (1 << (FB_WIDTH - 1)); i++) {
		fb_null(t[i]);
	}

	RLC_TRY {
		for (size_t i = 0; i < (1 << (FB_WIDTH - 1)); i ++) {
			fb_new(t[i]);
		}
		fb_new(r);

		fb_copy(t[0], a);
		fb_sqr(r, a);

		/* Create table. */
		for (size_t i = 1; i < 1 << (FB_WIDTH - 1); i++) {
			fb_mul(t[i], t[i - 1], r);
		}

		fb_set_dig(r, 1);
		l = RLC_FB_BITS + 1;
		bn_rec_slw(win, &l, b, FB_WIDTH);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				fb_sqr(r, r);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					fb_sqr(r, r);
				}
				fb_mul(r, r, t[win[i] >> 1]);
			}
		}

		if (bn_sign(b) == RLC_NEG) {
			fb_inv(c, r);
		} else {
			fb_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (size_t i = 0; i < (1 << (FB_WIDTH - 1)); i++) {
			fb_free(t[i]);
		}
		fb_free(r);
	}
}


static int valid_radix(unsigned int radix) {
	while (radix > 0) {
		if (radix != 1 && radix % 2 == 1)
			return 0;
		radix = radix / 2;
	}
	return 1;
}


size_t fb_size_str(const fb_t a, unsigned int radix) {
	bn_t t;
	size_t digits = 0;

	bn_null(t);

	if (!valid_radix(radix)) {
		RLC_THROW(ERR_NO_VALID);
		return 0;
	}

	RLC_TRY {
		bn_new(t);

		bn_read_raw(t, a, RLC_FB_DIGS);

		digits = bn_size_str(t, radix);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}

	return digits;
}


void fb_print(const fb_t a) {
	/* Suppress possible unused parameter warning. */
	(void)a;
	for (int i = RLC_FB_DIGS - 1; i > 0; i--) {
		util_print_dig(a[i], 1);
		util_print(" ");
	}
	util_print_dig(a[0], 1);
	util_print("\n");
}


void fb_write_bin(uint8_t *bin, size_t len, const fb_t a) {
	bn_t t;

	bn_null(t);

	if (len != RLC_FB_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	RLC_TRY {
		bn_new(t);

		bn_read_raw(t, a, RLC_FB_DIGS);

		bn_write_bin(bin, len, t);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void fb_set_bit(fb_t a, size_t bit, int value) {
	int d;
	dig_t mask;

	RLC_RIP(bit, d, bit);

	mask = (dig_t)1 << bit;

	if (value == 1) {
		a[d] |= mask;
	} else {
		a[d] &= ~mask;
	}
}


static int log_radix(unsigned int radix) {
	int l = 0;

	while (radix > 0) {
		radix = radix / 2;
		l++;
	}
	return --l;
}


void fb_read_bin(fb_t a, const uint8_t *bin, size_t len) {
	bn_t t;

	bn_null(t);

	if (len != RLC_FB_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	RLC_TRY {
		bn_new(t);

		bn_read_bin(t, bin, len);

		fb_copy(a, t->dp);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void fb_rand(fb_t a) {
	size_t bits, digits;

	rand_bytes((uint8_t *)a, RLC_FB_DIGS * sizeof(dig_t));

	RLC_RIP(bits, digits, RLC_FB_BITS);
	if (bits > 0) {
		dig_t mask = RLC_MASK(bits);
		a[RLC_FB_DIGS - 1] &= mask;
	}
}


void fb_write_str(char *str, size_t len, const fb_t a, unsigned int radix) {
	fb_t t;
	int d, l, i, j;
	char c;

	fb_null(t);

	l = fb_size_str(a, radix);
	if (len < l) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	len = l;

	l = log_radix(radix);
	if (!valid_radix(radix)) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	if (fb_is_zero(a) == 1) {
		*str++ = '0';
		*str = '\0';
		return;
	}

	RLC_TRY {
		fb_new(t);
		fb_copy(t, a);

		j = 0;
		while (!fb_is_zero(t)) {
			d = t[0] % radix;
			fb_rshb_low(t, t, l);
			str[j] = util_conv_char(d);
			j++;
		}

		/* Reverse the digits of the string. */
		i = 0;
		j = len - 2;
		while (i < j) {
			c = str[i];
			str[i] = str[j];
			str[j] = c;
			++i;
			--j;
		}

		str[len - 1] = '\0';
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fb_free(t);
	}
}


int fb_get_bit(const fb_t a, size_t bit) {
	int d;

	RLC_RIP(bit, d, bit);

	return (a[d] >> bit) & 1;
}


size_t fb_bits(const fb_t a) {
	int i = RLC_FB_DIGS - 1;

	while (i >= 0 && a[i] == 0) {
		i--;
	}

	if (i > 0) {
		return (i << RLC_DIG_LOG) + util_bits_dig(a[i]);
	} else {
		return util_bits_dig(a[0]);
	}
}


void fb_read_str(fb_t a, const char *str, size_t len, unsigned int radix) {
	bn_t t;

	bn_null(t);

	if (!valid_radix(radix)) {
		RLC_THROW(ERR_NO_VALID);
	}

	RLC_TRY {
		bn_new(t);

		bn_read_str(t, str, len, radix);

		if (bn_bits(t) > RLC_FB_BITS) {
			RLC_THROW(ERR_NO_BUFFER);
		}

		fb_zero(a);
		dv_copy(a, t->dp, t->used);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void fp_exp_basic(fp_t c, const fp_t a, const bn_t b) {
	size_t l;
	fp_t r;

	fp_null(r);

	if (bn_is_zero(b)) {
		fp_set_dig(c, 1);
		return;
	}

	RLC_TRY {
		fp_new(r);

		l = bn_bits(b);

		fp_copy(r, a);

		for (int i = l - 2; i >= 0; i--) {
			fp_sqr(r, r);
			if (bn_get_bit(b, i)) {
				fp_mul(r, r, a);
			}
		}

		if (bn_sign(b) == RLC_NEG) {
			fp_inv(c, r);
		} else {
			fp_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp_free(r);
	}
}


void fp_exp_slide(fp_t c, const fp_t a, const bn_t b) {
	fp_t t[1 << (FP_WIDTH - 1)], r;
	uint8_t win[RLC_FP_BITS + 1];
	size_t l;

	fp_null(r);

	if (bn_is_zero(b)) {
		fp_set_dig(c, 1);
		return;
	}

	/* Initialize table. */
	for (size_t i = 0; i < (1 << (FP_WIDTH - 1)); i++) {
		fp_null(t[i]);
	}

	RLC_TRY {
		for (size_t i = 0; i < (1 << (FP_WIDTH - 1)); i ++) {
			fp_new(t[i]);
		}
		fp_new(r);

		fp_copy(t[0], a);
		fp_sqr(r, a);

		/* Create table. */
		for (size_t i = 1; i < 1 << (FP_WIDTH - 1); i++) {
			fp_mul(t[i], t[i - 1], r);
		}

		fp_set_dig(r, 1);
		l = RLC_FP_BITS + 1;
		bn_rec_slw(win, &l, b, FP_WIDTH);
		for (size_t i = 0; i < l; i++) {
			if (win[i] == 0) {
				fp_sqr(r, r);
			} else {
				for (size_t j = 0; j < util_bits_dig(win[i]); j++) {
					fp_sqr(r, r);
				}
				fp_mul(r, r, t[win[i] >> 1]);
			}
		}

		if (bn_sign(b) == RLC_NEG) {
			fp_inv(c, r);
		} else {
			fp_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (size_t i = 0; i < (1 << (FP_WIDTH - 1)); i++) {
			fp_free(t[i]);
		}
		fp_free(r);
	}
}


void fp_prime_set_pmers(const int *f, size_t len) {
	bn_t p, t;

	bn_null(p);
	bn_null(t);

	RLC_TRY {
		bn_new(p);
		bn_new(t);

		if (len >= RLC_TERMS) {
			RLC_THROW(ERR_NO_VALID);
			return;
		}

		bn_set_2b(p, f[len - 1]);
		for (int i = len - 2; i > 0; i--) {
			if (f[i] > 0) {
				bn_set_2b(t, f[i]);
				bn_add(p, p, t);
			} else {
				bn_set_2b(t, -f[i]);
				bn_sub(p, p, t);
			}
		}
		if (f[0] > 0) {
			bn_add_dig(p, p, f[0]);
		} else {
			bn_sub_dig(p, p, -f[0]);
		}

#if FP_RDC == QUICK || !defined(STRIP)
		ctx_t *ctx = core_get();
		for (int i = 0; i < len; i++) {
			ctx->sps[i] = f[i];
		}
		ctx->sps[len] = 0;
		ctx->sps_len = len;
#endif /* FP_RDC == QUICK */

		fp_prime_set(p);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(p);
		bn_free(t);
	}
}


void fp_write_str(char *str, size_t len, const fp_t a, unsigned int radix) {
	bn_t t;

	bn_null(t);

	RLC_TRY {
		bn_new(t);

		fp_prime_back(t, a);

		bn_write_str(str, len, t, radix);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void fp_read_bin(fp_t a, const uint8_t *bin, size_t len) {
	bn_t t;

	bn_null(t);

	if (len != RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	RLC_TRY {
		bn_new(t);
		bn_read_bin(t, bin, len);

		/* Reject values out of bounds. */
		if (bn_sign(t) == RLC_NEG || bn_cmp(t, &core_get()->prime) != RLC_LT) {
			RLC_THROW(ERR_NO_VALID);
		} else {
			if (bn_is_zero(t)) {
				fp_zero(a);
			} else {
				if (t->used == 1) {
					fp_prime_conv_dig(a, t->dp[0]);
				} else {
					fp_prime_conv(a, t);
				}
			}
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


size_t fp_size_str(const fp_t a, unsigned int radix) {
	bn_t t;
	size_t digits = 0;

	bn_null(t);

	RLC_TRY {
		bn_new(t);

		fp_prime_back(t, a);

		digits = bn_size_str(t, radix);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}

	return digits;
}


void fp_read_str(fp_t a, const char *str, size_t len, unsigned int radix) {
	bn_t t;

	bn_null(t);

	RLC_TRY {
		bn_new(t);
		bn_read_str(t, str, len, radix);
		if (bn_is_zero(t)) {
			fp_zero(a);
		} else {
			if (t->used == 1) {
				fp_prime_conv_dig(a, t->dp[0]);
				if (bn_sign(t) == RLC_NEG) {
					fp_neg(a, a);
				}
			} else {
				fp_prime_conv(a, t);
			}
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void fp_set_bit(fp_t a, size_t bit, int value) {
	int d;
	dig_t mask;

	RLC_RIP(bit, d, bit);

	mask = (dig_t)1 << bit;

	if (value == 1) {
		a[d] |= mask;
	} else {
		a[d] &= ~mask;
	}
}


int fp_get_bit(const fp_t a, size_t bit) {
	int d;

	RLC_RIP(bit, d, bit);

	return (a[d] >> bit) & 1;
}


size_t fp_bits(const fp_t a) {
	int i = RLC_FP_DIGS - 1;

	while (i >= 0 && a[i] == 0) {
		i--;
	}

	if (i > 0) {
		return (i << RLC_DIG_LOG) + util_bits_dig(a[i]);
	} else {
		return util_bits_dig(a[0]);
	}
}


void fp_write_bin(uint8_t *bin, size_t len, const fp_t a) {
	bn_t t;

	bn_null(t);

	if (len != RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}

	RLC_TRY {
		bn_new(t);

		fp_prime_back(t, a);

		bn_write_bin(bin, len, t);
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(t);
	}
}


void fp48_exp_cyc_sps(fp48_t c, const fp48_t a, const int *b, size_t len,
		int sign) {
	size_t i, j, k, w = len;
    fp48_t t, *u = RLC_ALLOCA(fp48_t, w);

	if (len == 0) {
		RLC_FREE(u);
		fp48_set_dig(c, 1);
		return;
	}

	fp48_null(t);

	RLC_TRY {
		if (u == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (i = 0; i < w; i++) {
			fp48_null(u[i]);
			fp48_new(u[i]);
		}
		fp48_new(t);

		fp48_copy(t, a);
		if (b[0] == 0) {
			for (j = 0, i = 1; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp48_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp48_inv_cyc(u[i - 1], t);
				} else {
					fp48_copy(u[i - 1], t);
				}
			}

			fp48_back_cyc_sim(u, u, w - 1);

			fp48_copy(c, a);
			for (i = 0; i < w - 1; i++) {
				fp48_mul(c, c, u[i]);
			}
		} else {
			for (j = 0, i = 0; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp48_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp48_inv_cyc(u[i], t);
				} else {
					fp48_copy(u[i], t);
				}
			}

			fp48_back_cyc_sim(u, u, w);

			fp48_copy(c, u[0]);
			for (i = 1; i < w; i++) {
				fp48_mul(c, c, u[i]);
			}
		}

		if (sign == RLC_NEG) {
			fp48_inv_cyc(c, c);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i = 0; i < w; i++) {
			fp48_free(u[i]);
		}
		fp48_free(t);
		RLC_FREE(u);
	}
}


void fp8_exp_cyc(fp8_t c, const fp8_t a, const bn_t b) {
	fp8_t r, s, t[1 << (FP_WIDTH - 2)];
	int8_t naf[RLC_FP_BITS + 1], *k;
	size_t l;

	if (bn_is_zero(b)) {
		return fp8_set_dig(c, 1);
	}

	fp8_null(r);
	fp8_null(s);

	RLC_TRY {
		fp8_new(r);
		fp8_new(s);
		for (int i = 0; i < (1 << (FP_WIDTH - 2)); i ++) {
			fp8_null(t[i]);
			fp8_new(t[i]);
		}

#if FP_WIDTH > 2
		fp8_sqr_cyc(t[0], a);
		fp8_mul(t[1], t[0], a);
		for (int i = 2; i < (1 << (FP_WIDTH - 2)); i++) {
			fp8_mul(t[i], t[i - 1], t[0]);
		}
#endif
		fp8_copy(t[0], a);

		l = RLC_FP_BITS + 1;
		fp8_set_dig(r, 1);
		bn_rec_naf(naf, &l, b, FP_WIDTH);

		k = naf + l - 1;

		for (int i = l - 1; i >= 0; i--, k--) {
			fp8_sqr_cyc(r, r);

			if (*k > 0) {
				fp8_mul(r, r, t[*k / 2]);
			}
			if (*k < 0) {
				fp8_inv_cyc(s, t[-*k / 2]);
				fp8_mul(r, r, s);
			}
		}

		if (bn_sign(b) == RLC_NEG) {
			fp8_inv_cyc(c, r);
		} else {
			fp8_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp8_free(r);
		fp8_free(s);
		for (int i = 0; i < (1 << (FP_WIDTH - 2)); i++) {
			fp8_free(t[i]);
		}
	}
}


void fp2_exp_cyc(fp2_t c, const fp2_t a, const bn_t b) {
	fp2_t r, s, t[1 << (FP_WIDTH - 2)];
	int8_t naf[RLC_FP_BITS + 1], *k;
	size_t l;

	if (bn_is_zero(b)) {
		return fp2_set_dig(c, 1);
	}

	fp2_null(r);
	fp2_null(s);

	RLC_TRY {
		fp2_new(r);
		fp2_new(s);
		for (int i = 0; i < (1 << (FP_WIDTH - 2)); i ++) {
			fp2_null(t[i]);
			fp2_new(t[i]);
		}

#if FP_WIDTH > 2
		fp2_sqr(t[0], a);
		fp2_mul(t[1], t[0], a);
		for (int i = 2; i < (1 << (FP_WIDTH - 2)); i++) {
			fp2_mul(t[i], t[i - 1], t[0]);
		}
#endif
		fp2_copy(t[0], a);

		l = RLC_FP_BITS + 1;
		fp2_set_dig(r, 1);
		bn_rec_naf(naf, &l, b, FP_WIDTH);

		k = naf + l - 1;

		for (int i = l - 1; i >= 0; i--, k--) {
			fp2_sqr(r, r);

			if (*k > 0) {
				fp2_mul(r, r, t[*k / 2]);
			}
			if (*k < 0) {
				fp2_inv_cyc(s, t[-*k / 2]);
				fp2_mul(r, r, s);
			}
		}

		if (bn_sign(b) == RLC_NEG) {
			fp2_inv_cyc(c, r);
		} else {
			fp2_copy(c, r);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp2_free(r);
		fp2_free(s);
		for (int i = 0; i < (1 << (FP_WIDTH - 2)); i++) {
			fp2_free(t[i]);
		}
	}
}


void fp12_exp_cyc_sps(fp12_t c, const fp12_t a, const int *b, size_t len,
		int sign) {
	size_t i, j, k, w = len;
    fp12_t t, *u = RLC_ALLOCA(fp12_t, w);

	if (len == 0) {
		RLC_FREE(u);
		fp12_set_dig(c, 1);
		return;
	}

	fp12_null(t);

	RLC_TRY {
		if (u == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (i = 0; i < w; i++) {
			fp12_null(u[i]);
			fp12_new(u[i]);
		}
		fp12_new(t);

		fp12_copy(t, a);
		if (b[0] == 0) {
			for (j = 0, i = 1; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp12_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp12_inv_cyc(u[i - 1], t);
				} else {
					fp12_copy(u[i - 1], t);
				}
			}

			fp12_back_cyc_sim(u, u, w - 1);

			fp12_copy(c, a);
			for (i = 0; i < w - 1; i++) {
				fp12_mul(c, c, u[i]);
			}
		} else {
			for (j = 0, i = 0; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp12_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp12_inv_cyc(u[i], t);
				} else {
					fp12_copy(u[i], t);
				}
			}

			fp12_back_cyc_sim(u, u, w);

			fp12_copy(c, u[0]);
			for (i = 1; i < w; i++) {
				fp12_mul(c, c, u[i]);
			}
		}

		if (sign == RLC_NEG) {
			fp12_inv_cyc(c, c);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i = 0; i < w; i++) {
			fp12_free(u[i]);
		}
		fp12_free(t);
		RLC_FREE(u);
	}
}


void fp24_exp_cyc_sps(fp24_t c, const fp24_t a, const int *b, size_t len,
		int sign) {
	size_t i, j, k, w = len;
    fp24_t t, *u = RLC_ALLOCA(fp24_t, w);

	if (len == 0) {
		RLC_FREE(u);
		fp24_set_dig(c, 1);
		return;
	}

	fp24_null(t);

	RLC_TRY {
		if (u == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (i = 0; i < w; i++) {
			fp24_null(u[i]);
			fp24_new(u[i]);
		}
		fp24_new(t);

		fp24_copy(t, a);
		if (b[0] == 0) {
			for (j = 0, i = 1; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp24_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp24_inv_cyc(u[i - 1], t);
				} else {
					fp24_copy(u[i - 1], t);
				}
			}

			fp24_back_cyc_sim(u, u, w - 1);

			fp24_copy(c, a);
			for (i = 0; i < w - 1; i++) {
				fp24_mul(c, c, u[i]);
			}
		} else {
			for (j = 0, i = 0; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp24_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp24_inv_cyc(u[i], t);
				} else {
					fp24_copy(u[i], t);
				}
			}

			fp24_back_cyc_sim(u, u, w);

			fp24_copy(c, u[0]);
			for (i = 1; i < w; i++) {
				fp24_mul(c, c, u[i]);
			}
		}

		if (sign == RLC_NEG) {
			fp24_inv_cyc(c, c);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i = 0; i < w; i++) {
			fp24_free(u[i]);
		}
		fp24_free(t);
		RLC_FREE(u);
	}
}


void fp2_exp_cyc_sim(fp2_t e, const fp2_t a, const bn_t b, const fp2_t c, const bn_t d) {
	int n0, n1;
	int8_t naf0[RLC_FP_BITS + 1], naf1[RLC_FP_BITS + 1], *_k, *_m;
	fp2_t r, t0[1 << (EP_WIDTH - 2)];
	fp2_t s, t1[1 << (EP_WIDTH - 2)];
	size_t l, l0, l1;

	if (bn_is_zero(b)) {
		return fp2_exp_cyc(e, c, d);
	}

	if (bn_is_zero(d)) {
		return fp2_exp_cyc(e, a, b);
	}

	fp2_null(r);
	fp2_null(s);

	RLC_TRY {
		fp2_new(r);
		fp2_new(s);
		for (int i = 0; i < (1 << (FP_WIDTH - 2)); i ++) {
			fp2_null(t0[i]);
			fp2_null(t1[i]);
			fp2_new(t0[i]);
			fp2_new(t1[i]);
		}

#if FP_WIDTH > 2
		fp2_sqr(t0[0], a);
		fp2_mul(t0[1], t0[0], a);
		for (int i = 2; i < (1 << (FP_WIDTH - 2)); i++) {
			fp2_mul(t0[i], t0[i - 1], t0[0]);
		}

		fp2_sqr(t1[0], c);
		fp2_mul(t1[1], t1[0], c);
		for (int i = 2; i < (1 << (FP_WIDTH - 2)); i++) {
			fp2_mul(t1[i], t1[i - 1], t1[0]);
		}
#endif
		fp2_copy(t0[0], a);
		fp2_copy(t1[0], c);

		l0 = l1 = RLC_FP_BITS + 1;
		bn_rec_naf(naf0, &l0, b, FP_WIDTH);
		bn_rec_naf(naf1, &l1, d, FP_WIDTH);

		l = RLC_MAX(l0, l1);
		if (bn_sign(b) == RLC_NEG) {
			for (size_t i = 0; i < l0; i++) {
				naf0[i] = -naf0[i];
			}
		}
		if (bn_sign(d) == RLC_NEG) {
			for (size_t i = 0; i < l1; i++) {
				naf1[i] = -naf1[i];
			}
		}

		_k = naf0 + l - 1;
		_m = naf1 + l - 1;

		fp2_set_dig(r, 1);
		for (int i = l - 1; i >= 0; i--, _k--, _m--) {
			fp2_sqr(r, r);

			n0 = *_k;
			n1 = *_m;

			if (n0 > 0) {
				fp2_mul(r, r, t0[n0 / 2]);
			}
			if (n0 < 0) {
				fp2_inv_cyc(s, t0[-n0 / 2]);
				fp2_mul(r, r, s);
			}
			if (n1 > 0) {
				fp2_mul(r, r, t1[n1 / 2]);
			}
			if (n1 < 0) {
				fp2_inv_cyc(s, t1[-n1 / 2]);
				fp2_mul(r, r, s);
			}
		}

		fp2_copy(e, r);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp2_free(r);
		fp2_free(s);
		for (int i = 0; i < (1 << (FP_WIDTH - 2)); i++) {
			fp2_free(t0[i]);
			fp2_free(t1[i]);
		}
	}
}


void fp54_exp_cyc_sps(fp54_t c, const fp54_t a, const int *b, size_t len,
		int sign) {
	size_t i, j, k, w = len;
    fp54_t t, *u = RLC_ALLOCA(fp54_t, w);

	if (len == 0) {
		RLC_FREE(u);
		fp54_set_dig(c, 1);
		return;
	}

	fp54_null(t);

	RLC_TRY {
		if (u == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (i = 0; i < w; i++) {
			fp54_null(u[i]);
			fp54_new(u[i]);
		}
		fp54_new(t);

		fp54_copy(t, a);
		if (b[0] == 0) {
			for (j = 0, i = 1; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp54_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp54_inv_cyc(u[i - 1], t);
				} else {
					fp54_copy(u[i - 1], t);
				}
			}

			fp54_back_cyc_sim(u, u, w - 1);

			fp54_copy(c, a);
			for (i = 0; i < w - 1; i++) {
				fp54_mul(c, c, u[i]);
			}
		} else {
			for (j = 0, i = 0; i < len; i++) {
				k = (b[i] < 0 ? -b[i] : b[i]);
				for (; j < k; j++) {
					fp54_sqr_pck(t, t);
				}
				if (b[i] < 0) {
					fp54_inv_cyc(u[i], t);
				} else {
					fp54_copy(u[i], t);
				}
			}

			fp54_back_cyc_sim(u, u, w);

			fp54_copy(c, u[0]);
			for (i = 1; i < w; i++) {
				fp54_mul(c, c, u[i]);
			}
		}

		if (sign == RLC_NEG) {
			fp54_inv_cyc(c, c);
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		for (i = 0; i < w; i++) {
			fp54_free(u[i]);
		}
		fp54_free(t);
		RLC_FREE(u);
	}
}


void fp12_exp_dig(fp12_t c, const fp12_t a, dig_t b) {
	bn_t _b;
	fp12_t t, v;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	if (b == 0) {
		fp12_set_dig(c, 1);
		return;
	}

	bn_null(_b);
	fp12_null(t);
	fp12_null(v);

	RLC_TRY {
		bn_new(_b);
		fp12_new(t);
		fp12_new(v);

		fp12_copy(t, a);

		if (fp12_test_cyc(a)) {
			fp12_inv_cyc(v, a);
			bn_set_dig(_b, b);

			l = RLC_DIG + 1;
			bn_rec_naf(naf, &l, _b, 2);

			for (int i = bn_bits(_b) - 2; i >= 0; i--) {
				fp12_sqr_cyc(t, t);

				u = naf[i];
				if (u > 0) {
					fp12_mul(t, t, a);
				} else if (u < 0) {
					fp12_mul(t, t, v);
				}
			}
		} else {
			for (int i = util_bits_dig(b) - 2; i >= 0; i--) {
				fp12_sqr(t, t);
				if (b & ((dig_t)1 << i)) {
					fp12_mul(t, t, a);
				}
			}
		}

		fp12_copy(c, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(_b);
		fp12_free(t);
		fp12_free(v);
	}
}


void fp24_exp_dig(fp24_t c, const fp24_t a, dig_t b) {
	bn_t _b;
	fp24_t t, v;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	if (b == 0) {
		fp24_set_dig(c, 1);
		return;
	}

	bn_null(_b);
	fp24_null(t);
	fp24_null(v);

	RLC_TRY {
		bn_new(_b);
		fp24_new(t);
		fp24_new(v);

		fp24_copy(t, a);

		if (fp24_test_cyc(a)) {
			fp24_inv_cyc(v, a);
			bn_set_dig(_b, b);

			l = RLC_DIG + 1;
			bn_rec_naf(naf, &l, _b, 2);

			for (int i = bn_bits(_b) - 2; i >= 0; i--) {
				fp24_sqr_cyc(t, t);

				u = naf[i];
				if (u > 0) {
					fp24_mul(t, t, a);
				} else if (u < 0) {
					fp24_mul(t, t, v);
				}
			}
		} else {
			for (int i = util_bits_dig(b) - 2; i >= 0; i--) {
				fp24_sqr(t, t);
				if (b & ((dig_t)1 << i)) {
					fp24_mul(t, t, a);
				}
			}
		}

		fp24_copy(c, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(_b);
		fp24_free(t);
		fp24_free(v);
	}
}


void fp48_exp_dig(fp48_t c, const fp48_t a, dig_t b) {
	bn_t _b;
	fp48_t t, v;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	if (b == 0) {
		fp48_set_dig(c, 1);
		return;
	}

	bn_null(_b);
	fp48_null(t);
	fp48_null(v);

	RLC_TRY {
		bn_new(_b);
		fp48_new(t);
		fp48_new(v);

		fp48_copy(t, a);

		if (fp48_test_cyc(a)) {
			fp48_inv_cyc(v, a);
			bn_set_dig(_b, b);

			l = RLC_DIG + 1;
			bn_rec_naf(naf, &l, _b, 2);

			for (int i = bn_bits(_b) - 2; i >= 0; i--) {
				fp48_sqr_cyc(t, t);

				u = naf[i];
				if (u > 0) {
					fp48_mul(t, t, a);
				} else if (u < 0) {
					fp48_mul(t, t, v);
				}
			}
		} else {
			for (int i = util_bits_dig(b) - 2; i >= 0; i--) {
				fp48_sqr(t, t);
				if (b & ((dig_t)1 << i)) {
					fp48_mul(t, t, a);
				}
			}
		}

		fp48_copy(c, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(_b);
		fp48_free(t);
		fp48_free(v);
	}
}


void fp54_exp_dig(fp54_t c, const fp54_t a, dig_t b) {
	bn_t _b;
	fp54_t t, v;
	int8_t u, naf[RLC_DIG + 1];
	size_t l;

	if (b == 0) {
		fp54_set_dig(c, 1);
		return;
	}

	bn_null(_b);
	fp54_null(t);
	fp54_null(v);

	RLC_TRY {
		bn_new(_b);
		fp54_new(t);
		fp54_new(v);

		fp54_copy(t, a);

		if (fp54_test_cyc(a)) {
			fp54_inv_cyc(v, a);
			bn_set_dig(_b, b);

			l = RLC_DIG + 1;
			bn_rec_naf(naf, &l, _b, 2);

			for (int i = bn_bits(_b) - 2; i >= 0; i--) {
				fp54_sqr_cyc(t, t);

				u = naf[i];
				if (u > 0) {
					fp54_mul(t, t, a);
				} else if (u < 0) {
					fp54_mul(t, t, v);
				}
			}
		} else {
			for (int i = util_bits_dig(b) - 2; i >= 0; i--) {
				fp54_sqr(t, t);
				if (b & ((dig_t)1 << i)) {
					fp54_mul(t, t, a);
				}
			}
		}

		fp54_copy(c, t);
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		bn_free(_b);
		fp54_free(t);
		fp54_free(v);
	}
}


void fp6_read_bin(fp6_t a, const uint8_t *bin, size_t len) {
	if (len != 6 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp2_read_bin(a[0], bin, 2 * RLC_FP_BYTES);
	fp2_read_bin(a[1], bin + 2 * RLC_FP_BYTES, 2 * RLC_FP_BYTES);
	fp2_read_bin(a[2], bin + 4 * RLC_FP_BYTES, 2 * RLC_FP_BYTES);
}


void fp2_write_bin(uint8_t *bin, size_t len, const fp2_t a, int pack) {
	fp2_t t;

	fp2_null(t);

	RLC_TRY {
		fp2_new(t);

		if (pack && fp2_test_cyc(a)) {
			if (len < RLC_FP_BYTES + 1) {
				RLC_THROW(ERR_NO_BUFFER);
				return;
			} else {
				fp2_pck(t, a);
				fp_write_bin(bin, RLC_FP_BYTES, t[0]);
				bin[RLC_FP_BYTES] = fp_get_bit(t[1], 0);
			}
		} else {
			if (len < 2 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
				return;
			} else {
				fp_write_bin(bin, RLC_FP_BYTES, a[0]);
				fp_write_bin(bin + RLC_FP_BYTES, RLC_FP_BYTES, a[1]);
			}
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp2_free(t);
	}
}


void fp4_read_bin(fp4_t a, const uint8_t *bin, size_t len) {
	if (len != 4 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp2_read_bin(a[0], bin, 2 * RLC_FP_BYTES);
	fp2_read_bin(a[1], bin + 2 * RLC_FP_BYTES, 2 * RLC_FP_BYTES);
}


void fp3_write_bin(uint8_t *bin, size_t len, const fp3_t a) {
	if (len != 3 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp_write_bin(bin, RLC_FP_BYTES, a[0]);
	fp_write_bin(bin + RLC_FP_BYTES, RLC_FP_BYTES, a[1]);
	fp_write_bin(bin + 2 * RLC_FP_BYTES, RLC_FP_BYTES, a[2]);
}


void fp8_read_bin(fp8_t a, const uint8_t *bin, size_t len) {
	if (len != 8 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp4_read_bin(a[0], bin, 4 * RLC_FP_BYTES);
	fp4_read_bin(a[1], bin + 4 * RLC_FP_BYTES, 4 * RLC_FP_BYTES);
}


void fp24_write_bin(uint8_t *bin, size_t len, const fp24_t a, int pack) {
	fp24_t t;

	fp24_null(t);

	RLC_TRY {
		fp24_new(t);

		if (pack) {
			if (len != 16 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp24_pck(t, a);
			fp4_write_bin(bin, 4 * RLC_FP_BYTES, a[1][0]);
			fp4_write_bin(bin + 4 * RLC_FP_BYTES, 4 * RLC_FP_BYTES, a[1][1]);
			fp4_write_bin(bin + 8 * RLC_FP_BYTES, 4 * RLC_FP_BYTES, a[2][0]);
			fp4_write_bin(bin + 12 * RLC_FP_BYTES, 4 * RLC_FP_BYTES, a[2][1]);
		} else {
			if (len != 24 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp8_write_bin(bin, 8 * RLC_FP_BYTES, a[0]);
			fp8_write_bin(bin + 8 * RLC_FP_BYTES, 8 * RLC_FP_BYTES, a[1]);
			fp8_write_bin(bin + 16 * RLC_FP_BYTES, 8 * RLC_FP_BYTES, a[2]);
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		fp24_free(t);
	}
}


void fp48_read_bin(fp48_t a, const uint8_t *bin, size_t len) {
	if (len != 32 * RLC_FP_BYTES && len != 48 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	if (len == 32 * RLC_FP_BYTES) {
		fp8_zero(a[0][0]);
		fp8_read_bin(a[0][1], bin, 8 * RLC_FP_BYTES);
		fp8_read_bin(a[0][2], bin + 8 * RLC_FP_BYTES, 8 * RLC_FP_BYTES);
		fp8_read_bin(a[1][0], bin + 16 * RLC_FP_BYTES, 8 * RLC_FP_BYTES);
		fp8_zero(a[1][1]);
		fp8_read_bin(a[1][2], bin + 24 * RLC_FP_BYTES, 8 * RLC_FP_BYTES);
		fp48_back_cyc(a, a);
	}
	if (len == 48 * RLC_FP_BYTES) {
		fp24_read_bin(a[0], bin, 24 * RLC_FP_BYTES);
		fp24_read_bin(a[1], bin + 24 * RLC_FP_BYTES, 24 * RLC_FP_BYTES);
	}
}


void fp4_write_bin(uint8_t *bin, size_t len, const fp4_t a) {
	if (len != 4 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp2_write_bin(bin, 2 * RLC_FP_BYTES, a[0], 0);
	fp2_write_bin(bin + 2 * RLC_FP_BYTES, 2 * RLC_FP_BYTES, a[1], 0);
}


void fp6_write_bin(uint8_t *bin, size_t len, const fp6_t a) {
	if (len != 6 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp2_write_bin(bin, 2 * RLC_FP_BYTES, a[0], 0);
	fp2_write_bin(bin + 2 * RLC_FP_BYTES, 2 * RLC_FP_BYTES, a[1], 0);
	fp2_write_bin(bin + 4 * RLC_FP_BYTES, 2 * RLC_FP_BYTES, a[2], 0);
}


void fp24_read_bin(fp24_t a, const uint8_t *bin, size_t len) {
	if (len != 16 * RLC_FP_BYTES && len != 24 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	if (len == 16 * RLC_FP_BYTES) {
		fp4_zero(a[0][0]);
		fp4_zero(a[0][1]);
		fp4_read_bin(a[1][0], bin, 4 * RLC_FP_BYTES);
		fp4_read_bin(a[1][1], bin + 4 * RLC_FP_BYTES, 4 * RLC_FP_BYTES);
		fp4_read_bin(a[2][0], bin + 8 * RLC_FP_BYTES, 4 * RLC_FP_BYTES);
		fp4_read_bin(a[2][1], bin + 12 * RLC_FP_BYTES, 4 * RLC_FP_BYTES);
		fp24_back_cyc(a, a);
	}
	if (len == 24 * RLC_FP_BYTES) {
		fp8_read_bin(a[0], bin, 8 * RLC_FP_BYTES);
		fp8_read_bin(a[1], bin + 8 * RLC_FP_BYTES, 8 * RLC_FP_BYTES);
		fp8_read_bin(a[2], bin + 16 * RLC_FP_BYTES, 8 * RLC_FP_BYTES);
	}
}


void fp2_read_bin(fp2_t a, const uint8_t *bin, size_t len) {
	if (len != RLC_FP_BYTES + 1 && len != 2 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	if (len == RLC_FP_BYTES + 1) {
		fp_read_bin(a[0], bin, RLC_FP_BYTES);
		fp_zero(a[1]);
		fp_set_bit(a[1], 0, bin[RLC_FP_BYTES]);
		fp2_upk(a, a);
	}
	if (len == 2 * RLC_FP_BYTES) {
		fp_read_bin(a[0], bin, RLC_FP_BYTES);
		fp_read_bin(a[1], bin + RLC_FP_BYTES, RLC_FP_BYTES);
	}
}


void fp54_write_bin(uint8_t *bin, size_t len, const fp54_t a, int pack) {
	fp54_t t;

	fp54_null(t);

	RLC_TRY {
		fp54_new(t);

		if (pack) {
			if (len != 36 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp54_pck(t, a);
			fp9_write_bin(bin, 9 * RLC_FP_BYTES, a[1][0]);
			fp9_write_bin(bin + 9 * RLC_FP_BYTES, 9 * RLC_FP_BYTES, a[1][1]);
			fp9_write_bin(bin + 18 * RLC_FP_BYTES, 9 * RLC_FP_BYTES, a[2][0]);
			fp9_write_bin(bin + 27 * RLC_FP_BYTES, 9 * RLC_FP_BYTES, a[2][1]);
		} else {
			if (len != 54 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp18_write_bin(bin, 18 * RLC_FP_BYTES, a[0]);
			fp18_write_bin(bin + 18 * RLC_FP_BYTES, 18 * RLC_FP_BYTES, a[1]);
			fp18_write_bin(bin + 36 * RLC_FP_BYTES, 18 * RLC_FP_BYTES, a[2]);
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		fp54_free(t);
	}
}


void fp54_read_bin(fp54_t a, const uint8_t *bin, size_t len) {
	if (len != 36 * RLC_FP_BYTES && len != 54 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	if (len == 36 * RLC_FP_BYTES) {
		fp9_zero(a[0][0]);
		fp9_zero(a[0][1]);
		fp9_read_bin(a[1][0], bin, 9 * RLC_FP_BYTES);
		fp9_read_bin(a[1][1], bin + 9 * RLC_FP_BYTES, 9 * RLC_FP_BYTES);
		fp9_read_bin(a[2][0], bin + 18 * RLC_FP_BYTES, 9 * RLC_FP_BYTES);
		fp9_read_bin(a[2][1], bin + 27 * RLC_FP_BYTES, 9 * RLC_FP_BYTES);
		fp54_back_cyc(a, a);
	}
	if (len == 54 * RLC_FP_BYTES) {
		fp18_read_bin(a[0], bin, 18 * RLC_FP_BYTES);
		fp18_read_bin(a[1], bin + 18 * RLC_FP_BYTES, 18 * RLC_FP_BYTES);
		fp18_read_bin(a[2], bin + 36 * RLC_FP_BYTES, 18 * RLC_FP_BYTES);
	}
}


void fp3_read_bin(fp3_t a, const uint8_t *bin, size_t len) {
	if (len != 3 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp_read_bin(a[0], bin, RLC_FP_BYTES);
	fp_read_bin(a[1], bin + RLC_FP_BYTES, RLC_FP_BYTES);
	fp_read_bin(a[2], bin + 2 * RLC_FP_BYTES, RLC_FP_BYTES);
}


void fp8_write_bin(uint8_t *bin, size_t len, const fp8_t a) {
	if (len != 8 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp4_write_bin(bin, 4 * RLC_FP_BYTES, a[0]);
	fp4_write_bin(bin + 4 * RLC_FP_BYTES, 4 * RLC_FP_BYTES, a[1]);
}


void fp9_read_bin(fp9_t a, const uint8_t *bin, size_t len) {
	if (len != 9 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp3_read_bin(a[0], bin, 3 * RLC_FP_BYTES);
	fp3_read_bin(a[1], bin + 3 * RLC_FP_BYTES, 3 * RLC_FP_BYTES);
	fp3_read_bin(a[2], bin + 6 * RLC_FP_BYTES, 3 * RLC_FP_BYTES);
}


void fp12_read_bin(fp12_t a, const uint8_t *bin, size_t len) {
	if (len != 8 * RLC_FP_BYTES && len != 12 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	if (len == 8 * RLC_FP_BYTES) {
		fp2_zero(a[0][0]);
		fp2_read_bin(a[0][1], bin, 2 * RLC_FP_BYTES);
		fp2_read_bin(a[0][2], bin + 2 * RLC_FP_BYTES, 2 * RLC_FP_BYTES);
		fp2_read_bin(a[1][0], bin + 4 * RLC_FP_BYTES, 2 * RLC_FP_BYTES);
		fp2_zero(a[1][1]);
		fp2_read_bin(a[1][2], bin + 6 * RLC_FP_BYTES, 2 * RLC_FP_BYTES);
		fp12_back_cyc(a, a);
	}
	if (len == 12 * RLC_FP_BYTES) {
		fp6_read_bin(a[0], bin, 6 * RLC_FP_BYTES);
		fp6_read_bin(a[1], bin + 6 * RLC_FP_BYTES, 6 * RLC_FP_BYTES);
	}
}


void fp12_write_bin(uint8_t *bin, size_t len, const fp12_t a, int pack) {
	fp12_t t;

	fp12_null(t);

	RLC_TRY {
		fp12_new(t);

		if (pack) {
			if (len != 8 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp12_pck(t, a);
			fp2_write_bin(bin, 2 * RLC_FP_BYTES, a[0][1], 0);
			fp2_write_bin(bin + 2 * RLC_FP_BYTES, 2 * RLC_FP_BYTES, a[0][2], 0);
			fp2_write_bin(bin + 4 * RLC_FP_BYTES, 2 * RLC_FP_BYTES, a[1][0], 0);
			fp2_write_bin(bin + 6 * RLC_FP_BYTES, 2 * RLC_FP_BYTES, a[1][2], 0);
		} else {
			if (len != 12 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp6_write_bin(bin, 6 * RLC_FP_BYTES, a[0]);
			fp6_write_bin(bin + 6 * RLC_FP_BYTES, 6 * RLC_FP_BYTES, a[1]);
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		fp12_free(t);
	}
}


void fp9_write_bin(uint8_t *bin, size_t len, const fp9_t a) {
	if (len != 9 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp3_write_bin(bin, 3 * RLC_FP_BYTES, a[0]);
	fp3_write_bin(bin + 3 * RLC_FP_BYTES, 3 * RLC_FP_BYTES, a[1]);
	fp3_write_bin(bin + 6 * RLC_FP_BYTES, 3 * RLC_FP_BYTES, a[2]);
}


void fp48_write_bin(uint8_t *bin, size_t len, const fp48_t a, int pack) {
	fp48_t t;

	fp48_null(t);

	RLC_TRY {
		fp48_new(t);

		if (pack) {
			if (len != 32 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp48_pck(t, a);
			fp8_write_bin(bin, 8 * RLC_FP_BYTES, a[0][1]);
			fp8_write_bin(bin + 8 * RLC_FP_BYTES, 8 * RLC_FP_BYTES, a[0][2]);
			fp8_write_bin(bin + 16 * RLC_FP_BYTES, 8 * RLC_FP_BYTES, a[1][0]);
			fp8_write_bin(bin + 24 * RLC_FP_BYTES, 8 * RLC_FP_BYTES, a[1][2]);
		} else {
			if (len != 48 * RLC_FP_BYTES) {
				RLC_THROW(ERR_NO_BUFFER);
			}
			fp24_write_bin(bin, 24 * RLC_FP_BYTES, a[0], 0);
			fp24_write_bin(bin + 24 * RLC_FP_BYTES, 24 * RLC_FP_BYTES, a[1], 0);
		}
	} RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	} RLC_FINALLY {
		fp48_free(t);
	}
}


void fp18_read_bin(fp18_t a, const uint8_t *bin, size_t len) {
	if (len != 18 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp9_read_bin(a[0], bin, 9 * RLC_FP_BYTES);
	fp9_read_bin(a[1], bin + 9 * RLC_FP_BYTES, 9 * RLC_FP_BYTES);
}


void fp18_write_bin(uint8_t *bin, size_t len, const fp18_t a) {
	if (len != 18 * RLC_FP_BYTES) {
		RLC_THROW(ERR_NO_BUFFER);
		return;
	}
	fp9_write_bin(bin, 9 * RLC_FP_BYTES, a[0]);
	fp9_write_bin(bin + 9 * RLC_FP_BYTES, 9 * RLC_FP_BYTES, a[1]);
}


void md_map_b2s256(uint8_t *hash, const uint8_t *msg, size_t len) {
	memset(hash, 0, RLC_MD_LEN_B2S256);
	blake2s(hash, RLC_MD_LEN_B2S256, msg, len, NULL, 0);
}


void md_map_b2s160(uint8_t *hash, const uint8_t *msg, size_t len) {
	memset(hash, 0, RLC_MD_LEN_B2S160);
	blake2s(hash, RLC_MD_LEN_B2S160, msg, len, NULL, 0);
}


void md_hmac(uint8_t *mac, const uint8_t *in, size_t in_len, const uint8_t *key,
    size_t key_len) {
#if MD_MAP == SH224 || MD_MAP == SH256 || MD_MAP == B2S160 || MD_MAP == B2S256
  #define block_size 64
#elif MD_MAP == SH384 || MD_MAP == SH512
  #define block_size  128
#endif
    uint8_t opad[block_size + RLC_MD_LEN];
    uint8_t *ipad = RLC_ALLOCA(uint8_t, block_size + in_len);
	uint8_t _key[RLC_MAX(RLC_MD_LEN, block_size)];

    if (ipad == NULL) {
        RLC_THROW(ERR_NO_MEMORY);
		return;
    }

	if (key_len > block_size) {
		md_map(_key, key, key_len);
		key = _key;
		key_len = RLC_MD_LEN;
	}
	if (key_len <= block_size) {
		memcpy(_key, key, key_len);
		memset(_key + key_len, 0, block_size - key_len);
		key = _key;
	}
	for (int i = 0; i < block_size; i++) {
		opad[i] = 0x5C ^ key[i];
		ipad[i] = 0x36 ^ key[i];
	}
	memcpy(ipad + block_size, in, in_len);
	md_map(opad + block_size, ipad, block_size + in_len);
	md_map(mac, opad, block_size + RLC_MD_LEN);

    RLC_FREE(ipad);
}


void md_kdf(uint8_t *key, size_t key_len, const uint8_t *in, size_t in_len) {
	uint32_t i, j, d;
	uint8_t* buffer = RLC_ALLOCA(uint8_t, in_len + sizeof(uint32_t));
	uint8_t* t = RLC_ALLOCA(uint8_t, key_len + RLC_MD_LEN);
	if (buffer == NULL || t == NULL) {
		RLC_FREE(buffer);
		RLC_FREE(t);
		RLC_THROW(ERR_NO_MEMORY);
		return;
	}

	/* d = ceil(kLen/hLen). */
	d = RLC_CEIL(key_len, RLC_MD_LEN);
	memcpy(buffer, in, in_len);
	for (i = 1; i <= d; i++) {
		j = util_conv_big(i);
		/* c = integer_to_string(c, 4). */
		memcpy(buffer + in_len, &j, sizeof(uint32_t));
		/* t = t || hash(z || c). */
		md_map(t + (i - 1) * RLC_MD_LEN, buffer, in_len + sizeof(uint32_t));
	}
	memcpy(key, t, key_len);

	RLC_FREE(buffer);
	RLC_FREE(t);
}


void md_mgf(uint8_t *key, size_t key_len, const uint8_t *in, size_t in_len) {
	uint32_t i, j, d;
	uint8_t *buffer = RLC_ALLOCA(uint8_t, in_len + sizeof(uint32_t));
	uint8_t *t = RLC_ALLOCA(uint8_t, key_len + RLC_MD_LEN);

	if (buffer == NULL || t == NULL) {
		RLC_FREE(buffer);
		RLC_FREE(t);
		RLC_THROW(ERR_NO_MEMORY);
		return;
	}

	/* d = ceil(kLen/hLen). */
	d = RLC_CEIL(key_len, RLC_MD_LEN);
	memcpy(buffer, in, in_len);
	for (i = 0; i < d; i++) {
		j = util_conv_big(i);
		/* c = integer_to_string(c, 4). */
		memcpy(buffer + in_len, &j, sizeof(uint32_t));
		/* t = t || hash(z || c). */
		md_map(t + i * RLC_MD_LEN, buffer, in_len + sizeof(uint32_t));
	}
	memcpy(key, t, key_len);

	RLC_FREE(buffer);
	RLC_FREE(t);
}


void md_map_sh224(uint8_t *hash, const uint8_t *msg, size_t len) {
	SHA224Context ctx;

	if (SHA224Reset(&ctx) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA224Input(&ctx, msg, len) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA224Result(&ctx, hash) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
}


void md_map_sh256(uint8_t *hash, const uint8_t *msg, size_t len) {
	SHA256Context ctx;

	if (SHA256Reset(&ctx) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA256Input(&ctx, msg, len) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA256Result(&ctx, hash) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
}


void md_map_sh384(uint8_t *hash, const uint8_t *msg, size_t len) {
	SHA384Context ctx;

	if (SHA384Reset(&ctx) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA384Input(&ctx, msg, len) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA384Result(&ctx, hash) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
}


void md_map_sh512(uint8_t *hash, const uint8_t *msg, size_t len) {
	SHA512Context ctx;

	if (SHA512Reset(&ctx) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA512Input(&ctx, msg, len) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
	if (SHA512Result(&ctx, hash) != shaSuccess) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}
}


static void pp_mil_k12(fp12_t r, ep2_t *t, ep2_t *q, ep_t *p, int m, bn_t a) {
	fp12_t l;
	ep_t *_p = RLC_ALLOCA(ep_t, m);
	ep2_t *_q = RLC_ALLOCA(ep2_t, m);
	size_t len = bn_bits(a) + 1;
	int i, j;
	int8_t s[RLC_FP_BITS + 1];

	if (m == 0) {
		return;
	}

	fp12_null(l);

	RLC_TRY {
		fp12_new(l);
		if (_p == NULL || _q == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (j = 0; j < m; j++) {
			ep_null(_p[j]);
			ep2_null(_q[j]);
			ep_new(_p[j]);
			ep2_new(_q[j]);
			ep2_copy(t[j], q[j]);
			ep2_neg(_q[j], q[j]);
#if EP_ADD == BASIC
			ep_neg(_p[j], p[j]);
#else
			fp_add(_p[j]->x, p[j]->x, p[j]->x);
			fp_add(_p[j]->x, _p[j]->x, p[j]->x);
			fp_neg(_p[j]->y, p[j]->y);
#endif
		}

		fp12_zero(l);
		bn_rec_naf(s, &len, a, 2);
		pp_dbl_k12(r, t[0], t[0], _p[0]);
		for (j = 1; j < m; j++) {
			pp_dbl_k12(l, t[j], t[j], _p[j]);
			fp12_mul_dxs(r, r, l);
		}
		if (s[len - 2] > 0) {
			for (j = 0; j < m; j++) {
				pp_add_k12(l, t[j], q[j], p[j]);
				fp12_mul_dxs(r, r, l);
			}
		}
		if (s[len - 2] < 0) {
			for (j = 0; j < m; j++) {
				pp_add_k12(l, t[j], _q[j], p[j]);
				fp12_mul_dxs(r, r, l);
			}
		}

		for (i = len - 3; i >= 0; i--) {
			fp12_sqr(r, r);
			for (j = 0; j < m; j++) {
				pp_dbl_k12(l, t[j], t[j], _p[j]);
				fp12_mul_dxs(r, r, l);
				if (s[i] > 0) {
					pp_add_k12(l, t[j], q[j], p[j]);
					fp12_mul_dxs(r, r, l);
				}
				if (s[i] < 0) {
					pp_add_k12(l, t[j], _q[j], p[j]);
					fp12_mul_dxs(r, r, l);
				}
			}
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp12_free(l);
		for (j = 0; j < m; j++) {
			ep_free(_p[j]);
			ep2_free(_q[j]);
		}
		RLC_FREE(_p);
		RLC_FREE(_q);
	}
}


static void pp_mil_k24(fp24_t r, ep4_t *t, ep4_t *q, ep_t *p, int m, bn_t a) {
	fp24_t l;
	ep_t *_p = RLC_ALLOCA(ep_t, m);
	ep4_t *_q = RLC_ALLOCA(ep4_t, m);
	size_t len = bn_bits(a) + 1;
	int i, j;
	int8_t s[RLC_FP_BITS + 1];

	if (m == 0) {
		return;
	}

	fp24_null(l);

	RLC_TRY {
		fp24_new(l);
		if (_p == NULL || _q == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}
		for (j = 0; j < m; j++) {
			ep_null(_p[j]);
			ep4_null(_q[j]);
			ep_new(_p[j]);
			ep4_new(_q[j]);
			ep4_copy(t[j], q[j]);
			ep4_neg(_q[j], q[j]);
#if EP_ADD == BASIC
			ep_neg(_p[j], p[j]);
#else
			fp_add(_p[j]->x, p[j]->x, p[j]->x);
			fp_add(_p[j]->x, _p[j]->x, p[j]->x);
			fp_neg(_p[j]->y, p[j]->y);
#endif
		}

		fp24_zero(l);
		bn_rec_naf(s, &len, a, 2);
		pp_dbl_k24(r, t[0], t[0], _p[0]);
		for (j = 1; j < m; j++) {
			pp_dbl_k24(l, t[j], t[j], _p[j]);
			fp24_mul_dxs(r, r, l);
		}
		if (s[len - 2] > 0) {
			for (j = 0; j < m; j++) {
				pp_add_k24(l, t[j], q[j], p[j]);
				fp24_mul_dxs(r, r, l);
			}
		}
		if (s[len - 2] < 0) {
			for (j = 0; j < m; j++) {
				pp_add_k24(l, t[j], _q[j], p[j]);
				fp24_mul_dxs(r, r, l);
			}
		}

		for (i = len - 3; i >= 0; i--) {
			fp24_sqr(r, r);
			for (j = 0; j < m; j++) {
				pp_dbl_k24(l, t[j], t[j], _p[j]);
				fp24_mul_dxs(r, r, l);
				if (s[i] > 0) {
					pp_add_k24(l, t[j], q[j], p[j]);
					fp24_mul_dxs(r, r, l);
				}
				if (s[i] < 0) {
					pp_add_k24(l, t[j], _q[j], p[j]);
					fp24_mul_dxs(r, r, l);
				}
			}
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp24_free(l);
		for (j = 0; j < m; j++) {
			ep_free(_p[j]);
			ep4_free(_q[j]);
		}
		RLC_FREE(_p);
		RLC_FREE(_q);
	}
}


static void pp_mil_k48(fp48_t r, const fp8_t qx, const fp8_t qy, const ep_t p,
		const bn_t a) {
	fp48_t l;
	ep_t _p;
	fp8_t rx, ry, rz, qn;
	size_t len = bn_bits(a) + 1;
	int i;
	int8_t s[RLC_FP_BITS + 1];

	fp48_null(l);
	ep_null(_p);
	fp8_null(rx);
	fp8_null(ry);
	fp8_null(rz);
	fp8_null(qn);

	RLC_TRY {
		fp48_new(l);
		ep_new(_p);
		fp8_new(rx);
		fp8_new(ry);
		fp8_new(rz);
		fp8_new(qn);

		fp48_zero(l);
		fp8_copy(rx, qx);
		fp8_copy(ry, qy);
		fp8_set_dig(rz, 1);
#if EP_ADD == BASIC
		ep_neg(_p, p);
#else
		fp_add(_p->x, p->x, p->x);
		fp_add(_p->x, _p->x, p->x);
		fp_neg(_p->y, p->y);
#endif
		fp8_neg(qn, qy);

		bn_rec_naf(s, &len, a, 2);
		for (i = len - 2; i >= 0; i--) {
			fp48_sqr(r, r);
			pp_dbl_k48(l, rx, ry, rz, _p);
			fp48_mul_dxs(r, r, l);
			if (s[i] > 0) {
				pp_add_k48(l, rx, ry, rz, qx, qy, p);
				fp48_mul_dxs(r, r, l);
			}
			if (s[i] < 0) {
				pp_add_k48(l, rx, ry, rz, qx, qn, p);
				fp48_mul_dxs(r, r, l);
			}
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp48_free(l);
		ep_free(_p);
		fp8_free(rx);
		fp8_free(ry);
		fp8_free(rz);
		fp8_free(qn);
	}
}


static void pp_mil_k8(fp8_t r, ep2_t *t, ep2_t *q, ep_t *p, int m, bn_t a) {
	fp8_t l;
	ep_t *_p = RLC_ALLOCA(ep_t, m);
	ep2_t *_q = RLC_ALLOCA(ep2_t, m);
	size_t len = bn_bits(a) + 1;
	int i, j;
	int8_t s[RLC_FP_BITS + 1];

	if (m == 0) {
		return;
	}

	fp8_null(l);

	RLC_TRY {
		fp8_new(l);
		if (_p == NULL || _q == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
		}

		for (j = 0; j < m; j++) {
			ep_null(_p[j]);
			ep2_null(_q[j]);
			ep_new(_p[j]);
			ep2_new(_q[j]);
			ep2_copy(t[j], q[j]);
			ep2_neg(_q[j], q[j]);

#if EP_ADD == BASIC
			ep_neg(_p[j], p[j]);
#else
			fp_neg(_p[j]->x, p[j]->x);
			fp_copy(_p[j]->y, p[j]->y);
#endif
		}

		fp8_zero(l);
		bn_rec_naf(s, &len, a, 2);
		for (i = len - 2; i >= 0; i--) {
			fp8_sqr(r, r);
			for (j = 0; j < m; j++) {
				pp_dbl_k8(l, t[j], t[j], _p[j]);
				fp8_mul(r, r, l);
				if (s[i] > 0) {
					pp_add_k8(l, t[j], q[j], _p[j]);
					fp8_mul_dxs(r, r, l);
				}
				if (s[i] < 0) {
					pp_add_k8(l, t[j], _q[j], _p[j]);
					fp8_mul_dxs(r, r, l);
				}
			}
		}
	}
	RLC_CATCH_ANY {
		RLC_THROW(ERR_CAUGHT);
	}
	RLC_FINALLY {
		fp8_free(l);
		for (j = 0; j < m; j++) {
			ep_free(_p[j]);
			ep2_free(_q[j]);
		}
		RLC_FREE(_p);
		RLC_FREE(_q);
	}
}


int rand_check(uint8_t *buf, size_t size) {
	int count = 0;

	for (int i = 1; i < size; i++) {
		if (buf[i] == buf[i - 1]) {
			count++;
		} else {
			count = 0;
		}
	}

	if (count > RAND_REP) {
		return RLC_ERR;
	}
	return RLC_OK;
}


static int rand_add(uint8_t *state, uint8_t *hash, size_t size) {
	int carry = 0;
	for (int i = size - 1; i >= 0; i--) {
		/* Make sure carries are detected. */
		int16_t s;
		s = (state[i] + hash[i] + carry);
		state[i] = s & 0xFF;
		carry = s >> 8;
	}
	return carry;
}


void rand_bytes(uint8_t *buf, size_t size) {
	uint8_t hash[RLC_MD_LEN];
	int carry, len  = (RLC_RAND_SIZE - 1)/2;
	ctx_t *ctx = core_get();

	if (sizeof(int) > 2 && size > (1 << 16)) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	/* buf = hash_gen(size) */
	rand_gen(buf, size);
	/* H = hash(03 || V) */
	ctx->rand[0] = 0x3;
	md_map(hash, ctx->rand, 1 + len);
	/* V = V + H + C  + reseed_counter. */
	rand_add(ctx->rand + 1, ctx->rand + 1 + len, len);
	carry = rand_add(ctx->rand + 1 + (len - RLC_MD_LEN), hash, RLC_MD_LEN);
	rand_inc(ctx->rand, len - RLC_MD_LEN + 1, carry);
	rand_inc(ctx->rand, len + 1, ctx->counter);
	ctx->counter = ctx->counter + 1;
}


void rand_seed(uint8_t *buf, size_t size) {
	ctx_t *ctx = core_get();
	size_t len = (RLC_RAND_SIZE - 1) / 2;

	if (size <= 0) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	if (sizeof(int) > 4 && size > (1 << 32)) {
		RLC_THROW(ERR_NO_VALID);
		return;
	}

	ctx->rand[0] = 0x0;
	if (ctx->seeded == 0) {
		/* V = hash_df(seed). */
		rand_hash(ctx->rand + 1, len, buf, size);
		/* C = hash_df(00 || V). */
		rand_hash(ctx->rand + 1 + len, len, ctx->rand, len + 1);
	} else {
		/* V = hash_df(01 || V || seed). */
        int tmp_size = 1 + len + size;
		uint8_t* tmp = RLC_ALLOCA(uint8_t, tmp_size);
		if (tmp == NULL) {
			RLC_THROW(ERR_NO_MEMORY);
			return;
		}
		tmp[0] = 1;
		memcpy(tmp + 1, ctx->rand + 1, len);
		memcpy(tmp + 1 + len, buf, size);
		rand_hash(ctx->rand + 1, len, tmp, tmp_size);
		/* C = hash_df(00 || V). */
		rand_hash(ctx->rand + 1 + len, len, ctx->rand, len + 1);
		RLC_FREE(tmp);
	}
	ctx->counter = ctx->seeded = 1;
}


static void rand_gen(uint8_t *out, size_t out_len) {
	int m = RLC_CEIL(out_len, RLC_MD_LEN);
	uint8_t hash[RLC_MD_LEN], data[(RLC_RAND_SIZE - 1)/2];
	ctx_t *ctx = core_get();

	/* data = V */
	memcpy(data, ctx->rand + 1, (RLC_RAND_SIZE - 1)/2);
	for (int i = 0; i < m; i++) {
		/* w_i = Hash(data) */
		md_map(hash, data, sizeof(data));
		/* W = W || w_i */
		memcpy(out, hash, RLC_MIN(RLC_MD_LEN, out_len));
		out += RLC_MD_LEN;
		out_len -= RLC_MD_LEN;
		/* data = data + 1 mod 2^b. */
		rand_inc(data, (RLC_RAND_SIZE - 1)/2, 1);
	}
}


static int rand_inc(uint8_t *data, size_t size, int digit) {
	int carry = digit;
	for (int i = size - 1; i >= 0; i--) {
		int16_t s;
		s = (data[i] + carry);
		data[i] = s & 0xFF;
		carry = s >> 8;
	}
	return carry;
}


static void rand_hash(uint8_t *out, size_t out_len, uint8_t *in,
		size_t in_len) {
	uint32_t j = util_conv_big(8 * out_len);
	size_t len = RLC_CEIL(out_len, RLC_MD_LEN);
	uint8_t* buf = RLC_ALLOCA(uint8_t, 1 + sizeof(uint32_t) + in_len);
	uint8_t hash[RLC_MD_LEN];

	if (buf == NULL) {
		RLC_THROW(ERR_NO_MEMORY);
		return;
	}

	buf[0] = 1;
	memcpy(buf + 1, &j, sizeof(uint32_t));
	memcpy(buf + 1 + sizeof(uint32_t), in, in_len);

	for (int i = 0; i < len; i++) {
		/* h = Hash(counter || bits_to_return || input_string) */
		md_map(hash, buf, 1 + sizeof(uint32_t) + in_len);
		/* temp = temp || h */
		memcpy(out, hash, RLC_MIN(RLC_MD_LEN, out_len));
		out += RLC_MD_LEN;
		out_len -= RLC_MD_LEN;
		/* counter = counter + 1 */
		buf[0]++;
	}

	RLC_FREE(buf);
}


size_t util_bits_dig(dig_t a) {
    return RLC_DIG - arch_lzcnt(a);
}


static int square_root(void) {
	size_t bits;
	bn_t a, b, c;
	int code = RLC_ERR;

	bn_null(a);
	bn_null(b);
	bn_null(c);

	RLC_TRY {
		bn_new(a);
		bn_new(b);
		bn_new(c);

		TEST_ONCE("square root extraction is correct") {
			for (bits = 0; bits < RLC_BN_BITS / 2; bits++) {
				bn_rand(a, RLC_POS, bits);
				bn_sqr(c, a);
				bn_srt(b, c);
				TEST_ASSERT(bn_cmp(a, b) == RLC_EQ, end);
			}
			for (bits = 0; bits < RLC_BN_BITS; bits++) {
				bn_rand(a, RLC_POS, bits);
				bn_srt(b, a);
				bn_sqr(c, b);
				TEST_ASSERT(bn_cmp(c, a) != RLC_GT, end);
			}
		}
		TEST_END;

		TEST_ONCE("square root of powers of 2 is correct") {
			for (bits = 0; bits < RLC_BN_BITS / 2; bits++) {
				bn_set_2b(a, bits);
				bn_sqr(c, a);
				bn_srt(b, c);
				TEST_ASSERT(bn_cmp(a, b) == RLC_EQ, end);
			}
		}
		TEST_END;
	}
	RLC_CATCH_ANY {
		RLC_ERROR(end);
	}
	code = RLC_OK;
  end:
	bn_free(a);
	bn_free(b);
	bn_free(c);
	return code;
}


int util(void) {
	int code = RLC_ERR;
	gt_t a, b, c;
	uint8_t bin[24 * RLC_PC_BYTES];

	gt_null(a);
	gt_null(b);
	gt_null(c);

	RLC_TRY {
		gt_new(a);
		gt_new(b);
		gt_new(c);

		TEST_CASE("comparison is consistent") {
			gt_rand(a);
			gt_rand(b);
			TEST_ASSERT(gt_cmp(a, b) != RLC_EQ, end);
		}
		TEST_END;

		TEST_CASE("copy and comparison are consistent") {
			gt_rand(a);
			gt_rand(b);
			gt_rand(c);
			if (gt_cmp(a, c) != RLC_EQ) {
				gt_copy(c, a);
				TEST_ASSERT(gt_cmp(c, a) == RLC_EQ, end);
			}
			if (gt_cmp(b, c) != RLC_EQ) {
				gt_copy(c, b);
				TEST_ASSERT(gt_cmp(b, c) == RLC_EQ, end);
			}
		}
		TEST_END;

		TEST_CASE("inversion and comparison are consistent") {
			gt_rand(a);
			gt_inv(b, a);
			TEST_ASSERT(gt_cmp(a, b) != RLC_EQ, end);
		}
		TEST_END;

		TEST_CASE
				("assignment to random/infinity and comparison are consistent")
		{
			gt_rand(a);
			gt_set_unity(c);
			TEST_ASSERT(gt_cmp(a, c) != RLC_EQ, end);
			TEST_ASSERT(gt_cmp(c, a) != RLC_EQ, end);
		}
		TEST_END;

		TEST_CASE("assignment to unity and unity test are consistent") {
			gt_set_unity(a);
			TEST_ASSERT(gt_is_unity(a), end);
		}
		TEST_END;
	}
	RLC_CATCH_ANY {
		util_print("FATAL ERROR!\n");
		RLC_ERROR(end);
	}
	code = RLC_OK;
  end:
	gt_free(a);
	gt_free(b);
	gt_free(c);
	return code;
}


static int test(void) {
	uint8_t out[64];
	size_t len = sizeof(out) / 2, code = RLC_ERR;

	TEST_ONCE("rdrand hardware generator is non-trivial") {
		memset(out, 0, 2 * len);
		rand_bytes(out, len);
		/* This fails with negligible probability. */
		TEST_ASSERT(memcmp(out, out + len, len) != 0, end);
	}
	TEST_END;

	code = RLC_OK;

  end:
	return code;
}


static int restrictedFile(const char *filename)
{
	char *stripped_filename;
	RAII_VAR(char *, path, NULL, ast_free);
	RAII_VAR(char *, real_path, NULL, ast_free);

	if (live_dangerously) {
		return 0;
	}

	stripped_filename = ast_strip(ast_strdupa(filename));

	/* If the file path starts with '/', don't prepend ast_config_AST_CONFIG_DIR */
	if (stripped_filename[0] == '/') {
		real_path = realpath(stripped_filename, NULL);
	} else {
		if (ast_asprintf(&path, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
			return -1;
		}
		real_path = realpath(path, NULL);
	}

	if (!real_path) {
		return -1;
	}

	if (!ast_begins_with(real_path, ast_config_AST_CONFIG_DIR)) {
		return 1;
	}

	return 0;
}

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.


Suite *cjose_jwe_suite()
{
    Suite *suite = suite_create("jwe");

    TCase *tc_jwe = tcase_create("core");
    tcase_set_timeout(tc_jwe, 120.0);
    tcase_add_test(tc_jwe, test_cjose_jwe_node_jose_encrypt_self_decrypt);
    tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt);
    tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_iv);
    tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_short);
    tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_empty);
    tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_large);
    tcase_add_test(tc_jwe, test_cjose_jwe_self_encrypt_self_decrypt_many);
    tcase_add_test(tc_jwe, test_cjose_jwe_decrypt_aes);
    tcase_add_test(tc_jwe, test_cjose_jwe_decrypt_aes_gcm);
    tcase_add_test(tc_jwe, test_cjose_jwe_decrypt_rsa);
    tcase_add_test(tc_jwe, test_cjose_jwe_encrypt_with_bad_header);
    tcase_add_test(tc_jwe, test_cjose_jwe_encrypt_with_bad_key);
    tcase_add_test(tc_jwe, test_cjose_jwe_encrypt_with_bad_content);
    tcase_add_test(tc_jwe, test_cjose_jwe_import_export_compare);
    tcase_add_test(tc_jwe, test_cjose_jwe_import_invalid_serialization);
    tcase_add_test(tc_jwe, test_cjose_jwe_decrypt_bad_params);
    tcase_add_test(tc_jwe, test_cjose_jwe_multiple_recipients);
    suite_add_tcase(suite, tc_jwe);

    return suite;
}

Use of a Broken or Risky Cryptographic Algorithm

The product uses a broken or risky cryptographic algorithm or protocol.


OE_INLINE void _handle_oret(
    oe_sgx_td_t* td,
    uint16_t func,
    uint16_t result,
    uint64_t arg)
{
    oe_callsite_t* callsite = td->callsites;

    if (!callsite)
        return;

    td->oret_func = func;
    td->oret_result = result;
    td->oret_arg = arg;

    /* Restore the FXSTATE and flags */
    asm volatile(
        "pushq %[rflags] \n\t" // Restore flags.
        "popfq \n\t"
        "fldcw %[fcw] \n\t"     // Restore x87 control word
        "ldmxcsr %[mxcsr] \n\t" // Restore MXCSR
        "lfence \n\t" // MXCSR Configuration Dependent Timing (MCDT) mitigation
        : [mxcsr] "=m"(callsite->mxcsr),
          [fcw] "=m"(callsite->fcw),
          [rflags] "=m"(callsite->rflags)
        :
        : "cc");

    oe_longjmp(&callsite->jmpbuf, 1);
}

The product does not initialize or incorrectly initializes a resource, which might leave the resource in an unexpected state when it is accessed or used.


void _reset_fxsave_state()
{
    /* Initialize the FXSAVE state values to Linux x86-64 ABI defined values:
     * FCW = 0x037F, MXCSR = 0x1FBF, MXCSR mask = 0xFFFF */
    static OE_ALIGNED(OE_FXSAVE_ALIGNMENT) const uint64_t
        _initial_fxstate[OE_FXSAVE_AREA_SIZE / sizeof(uint64_t)] = {
            0x037F, 0,          0, 0xFFFF00001FBF,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            0,      0,          0, 0,
            2,      0x80000002, 0, 0,
            0,      0,          0, 0,
        };

    asm volatile("fxrstor %[fx_state] \n\t"
                 :
                 : [fx_state] "m"(_initial_fxstate)
                 :);
}


JSON_read(int fd)
{
    uint32_t hsize, nsize;
    size_t strsize;
    char *str;
    cJSON *json = NULL;
    int rc;

    /*
     * Read a four-byte integer, which is the length of the JSON to follow.
     * Then read the JSON into a buffer and parse it.  Return a parsed JSON
     * structure, NULL if there was an error.
     */
    if (Nread(fd, (char*) &nsize, sizeof(nsize), Ptcp) >= 0) {
	hsize = ntohl(nsize);
	/* Allocate a buffer to hold the JSON */
	strsize = hsize + 1;              /* +1 for trailing NULL */
	if (strsize) {
	str = (char *) calloc(sizeof(char), strsize);
	if (str != NULL) {
	    rc = Nread(fd, str, hsize, Ptcp);
	    if (rc >= 0) {
		/*
		 * We should be reading in the number of bytes corresponding to the
		 * length in that 4-byte integer.  If we don't the socket might have
		 * prematurely closed.  Only do the JSON parsing if we got the
		 * correct number of bytes.
		 */
		if (rc == hsize) {
		    json = cJSON_Parse(str);
		}
		else {
		    printf("WARNING:  Size of data read does not correspond to offered length\n");
		}
	    }
	}
	free(str);
	}
	else {
	    printf("WARNING:  Data length overflow\n");
	}
    }
    return json;
}


static BOOL nsc_stream_initialize(NSC_CONTEXT* context, wStream* s)
{
	WINPR_ASSERT(context);
	WINPR_ASSERT(context->priv);
	if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 20))
		return FALSE;

	size_t total = 0;
	for (size_t i = 0; i < 4; i++)
	{
		Stream_Read_UINT32(s, context->PlaneByteCount[i]);
		total += context->PlaneByteCount[i];
	}

	Stream_Read_UINT8(s, context->ColorLossLevel);         /* ColorLossLevel (1 byte) */
	Stream_Read_UINT8(s, context->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */
	Stream_Seek(s, 2);                                     /* Reserved (2 bytes) */
	context->Planes = Stream_Pointer(s);
	context->PlanesSize = total;
	return Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, total);
}

The product reads data past the end, or before the beginning, of the intended buffer.


static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
{
	if (!context)
		return FALSE;

	const BYTE* rle = context->Planes;
	size_t rleSize = context->PlanesSize;
	WINPR_ASSERT(rle);

	for (size_t i = 0; i < 4; i++)
	{
		const UINT32 originalSize = context->OrgByteCount[i];
		const UINT32 planeSize = context->PlaneByteCount[i];

		if (rleSize < planeSize)
			return FALSE;

		if (planeSize == 0)
		{
			if (context->priv->PlaneBuffersLength < originalSize)
				return FALSE;

			FillMemory(context->priv->PlaneBuffers[i], originalSize, 0xFF);
		}
		else if (planeSize < originalSize)
		{
			if (!nsc_rle_decode(rle, rleSize, context->priv->PlaneBuffers[i],
			                    context->priv->PlaneBuffersLength, originalSize))
				return FALSE;
		}
		else
		{
			if (context->priv->PlaneBuffersLength < originalSize)
				return FALSE;

			if (rleSize < originalSize)
				return FALSE;

			CopyMemory(context->priv->PlaneBuffers[i], rle, originalSize);
		}

		rle += planeSize;
	}

	return TRUE;
}


static BOOL nsc_rle_decode(const BYTE* in, size_t inSize, BYTE* out, UINT32 outSize,
                           UINT32 originalSize)
{
	UINT32 left = originalSize;

	while (left > 4)
	{
		if (inSize < 1)
			return FALSE;
		inSize--;

		const BYTE value = *in++;
		UINT32 len = 0;

		if (left == 5)
		{
			if (outSize < 1)
				return FALSE;

			outSize--;
			*out++ = value;
			left--;
		}
		else if (inSize < 1)
			return FALSE;
		else if (value == *in)
		{
			inSize--;
			in++;

			if (inSize < 1)
				return FALSE;
			else if (*in < 0xFF)
			{
				inSize--;
				len = (UINT32)*in++;
				len += 2;
			}
			else
			{
				if (inSize < 5)
					return FALSE;
				inSize -= 5;
				in++;
				len = ((UINT32)(*in++));
				len |= ((UINT32)(*in++)) << 8U;
				len |= ((UINT32)(*in++)) << 16U;
				len |= ((UINT32)(*in++)) << 24U;
			}

			if (outSize < len)
				return FALSE;

			outSize -= len;
			FillMemory(out, len, value);
			out += len;
			left -= len;
		}
		else
		{
			if (outSize < 1)
				return FALSE;

			outSize--;
			*out++ = value;
			left--;
		}
	}

	if ((outSize < 4) || (left < 4))
		return FALSE;

	if (inSize < 4)
		return FALSE;
	memcpy(out, in, 4);
	return TRUE;
}


static int ncrush_generate_tables(NCRUSH_CONTEXT* context)
{
	UINT32 k, i;
	int j, l;
	k = 0;

	WINPR_ASSERT(context);
	WINPR_ASSERT(28 < ARRAYSIZE(LOMBitsLUT));

	for (i = 0; i < 28; i++)
	{
		for (j = 0; j < 1 << LOMBitsLUT[i]; j++)
		{
			l = (k++) + 2;
			context->HuffTableLOM[l] = (int)i;
		}
	}

	for (k = 2; k < 4096; k++)
	{
		if ((k - 2) >= 768)
			i = 28;
		else
			i = context->HuffTableLOM[k];

		if (i >= ARRAYSIZE(LOMBitsLUT))
			return -1;
		if (i >= ARRAYSIZE(LOMBaseLUT))
			return -1;

		if (((((1 << LOMBitsLUT[i]) - 1) & (k - 2)) + LOMBaseLUT[i]) != k)
			return -1;
	}

	k = 0;

	for (i = 0; i < 16; i++)
	{
		for (j = 0; j < 1 << CopyOffsetBitsLUT[i]; j++)
		{
			l = k++ + 2;
			context->HuffTableCopyOffset[l] = i;
		}
	}

	k /= 128;

	for (i = 16; i < 32; i++)
	{
		for (j = 0; j < 1 << (CopyOffsetBitsLUT[i] - 7); j++)
		{
			l = k++ + 2 + 256;
			context->HuffTableCopyOffset[l] = i;
		}
	}

	if ((k + 256) > 1024)
		return -1;

	return 1;
}

Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.