示例#1
0
/**
 * Applies or removes simple encryption padding.
 *
 * @param[out] m		- the buffer to pad.
 * @param[out] p_len	- the number of added pad bytes.
 * @param[in] m_len		- the message length in bytes.
 * @param[in] k_len		- the key length in bytes.
 * @param[in] operation	- flag to indicate the operation type.
 * @return STS_ERR if errors occurred, STS_OK otherwise.
 */
static int pad_basic(bn_t m, int *p_len, int m_len, int k_len, int operation) {
	uint8_t pad = 0;
	int result = STS_OK;
	bn_t t;

	TRY {
		bn_null(t);
		bn_new(t);

		switch (operation) {
			case RSA_ENC:
			case RSA_SIG:
			case RSA_SIG_HASH:
				/* EB = 00 | FF | D. */
				bn_zero(m);
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, RSA_PAD);
				/* Make room for the real message. */
				bn_lsh(m, m, m_len * 8);
				break;
			case RSA_DEC:
			case RSA_VER:
			case RSA_VER_HASH:
				/* EB = 00 | FF | D. */
				m_len = k_len - 1;
				bn_rsh(t, m, 8 * m_len);
				if (!bn_is_zero(t)) {
					result = STS_ERR;
				}
				*p_len = 1;
				do {
					(*p_len)++;
					m_len--;
					bn_rsh(t, m, 8 * m_len);
					pad = (uint8_t)t->dp[0];
				} while (pad == 0);
				if (pad != RSA_PAD) {
					result = STS_ERR;
				}
				bn_mod_2b(m, m, (k_len - *p_len) * 8);
				break;
		}
	}
	CATCH_ANY {
		result = STS_ERR;
	}
	FINALLY {
		bn_free(t);
	}
	return result;
}
示例#2
0
int cp_ecss_sig(bn_t e, bn_t s, uint8_t *msg, int len, bn_t d) {
	bn_t n, k, x, r;
	ec_t p;
	uint8_t hash[MD_LEN];
	uint8_t m[len + FC_BYTES];
	int result = STS_OK;

	bn_null(n);
	bn_null(k);
	bn_null(x);
	bn_null(r);
	ec_null(p);

	TRY {
		bn_new(n);
		bn_new(k);
		bn_new(x);
		bn_new(r);
		ec_new(p);

		ec_curve_get_ord(n);
		do {
			bn_rand_mod(k, n);
			ec_mul_gen(p, k);
			ec_get_x(x, p);
			bn_mod(r, x, n);
		} while (bn_is_zero(r));

		memcpy(m, msg, len);
		bn_write_bin(m + len, FC_BYTES, r);
		md_map(hash, m, len + FC_BYTES);

		if (8 * MD_LEN > bn_bits(n)) {
			len = CEIL(bn_bits(n), 8);
			bn_read_bin(e, hash, len);
			bn_rsh(e, e, 8 * MD_LEN - bn_bits(n));
		} else {
			bn_read_bin(e, hash, MD_LEN);
		}

		bn_mod(e, e, n);

		bn_mul(s, d, e);
		bn_mod(s, s, n);
		bn_sub(s, n, s);
		bn_add(s, s, k);
		bn_mod(s, s, n);
	}
	CATCH_ANY {
		result = STS_ERR;
	}
	FINALLY {
		bn_free(n);
		bn_free(k);
		bn_free(x);
		bn_free(r);
		ec_free(p);
	}
	return result;
}
void bn_gen_prime_safep(bn_t a, int bits) {
	while (1) {
		do {
			bn_rand(a, BN_POS, bits);
		} while (bn_bits(a) != bits);
		/* Check if (a - 1)/2 is prime. */
		bn_sub_dig(a, a, 1);
		bn_rsh(a, a, 1);
		if (bn_is_prime(a)) {
			/* Restore a. */
			bn_lsh(a, a, 1);
			bn_add_dig(a, a, 1);
			if (bn_is_prime(a)) {
				/* Should be prime now. */
				return;
			}
		}
	}
}
示例#4
0
/**
 * Applies or removes a PKCS#1 v2.1 encryption padding.
 *
 * @param[out] m		- the buffer to pad.
 * @param[out] p_len	- the number of added pad bytes.
 * @param[in] m_len		- the message length in bytes.
 * @param[in] k_len		- the key length in bytes.
 * @param[in] operation	- flag to indicate the operation type.
 * @return STS_ERR if errors occurred, STS_OK otherwise.
 */
static int pad_pkcs2(bn_t m, int *p_len, int m_len, int k_len, int operation) {
	uint8_t pad, h1[MD_LEN], h2[MD_LEN], mask[k_len];
	int result = STS_OK;
	bn_t t;

	bn_null(t);

	TRY {
		bn_new(t);

		switch (operation) {
			case RSA_ENC:
				/* DB = lHash | PS | 01 | D. */
				md_map(h1, NULL, 0);
				bn_read_bin(m, h1, MD_LEN);
				*p_len = k_len - 2 * MD_LEN - 2 - m_len;
				bn_lsh(m, m, *p_len * 8);
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, 0x01);
				/* Make room for the real message. */
				bn_lsh(m, m, m_len * 8);
				break;
			case RSA_ENC_FIN:
				/* EB = 00 | maskedSeed | maskedDB. */
				rand_bytes(h1, MD_LEN);
				md_mgf1(mask, k_len - MD_LEN - 1, h1, MD_LEN);
				bn_read_bin(t, mask, k_len - MD_LEN - 1);
				for (int i = 0; i < t->used; i++) {
					m->dp[i] ^= t->dp[i];
				}
				bn_write_bin(mask, k_len - MD_LEN - 1, m);
				md_mgf1(h2, MD_LEN, mask, k_len - MD_LEN - 1);
				for (int i = 0; i < MD_LEN; i++) {
					h1[i] ^= h2[i];
				}
				bn_read_bin(t, h1, MD_LEN);
				bn_lsh(t, t, 8 * (k_len - MD_LEN - 1));
				bn_add(t, t, m);
				bn_copy(m, t);
				break;
			case RSA_DEC:
				m_len = k_len - 1;
				bn_rsh(t, m, 8 * m_len);
				if (!bn_is_zero(t)) {
					result = STS_ERR;
				}
				m_len -= MD_LEN;
				bn_rsh(t, m, 8 * m_len);
				bn_write_bin(h1, MD_LEN, t);
				bn_mod_2b(m, m, 8 * m_len);
				bn_write_bin(mask, m_len, m);
				md_mgf1(h2, MD_LEN, mask, m_len);
				for (int i = 0; i < MD_LEN; i++) {
					h1[i] ^= h2[i];
				}
				md_mgf1(mask, k_len - MD_LEN - 1, h1, MD_LEN);
				bn_read_bin(t, mask, k_len - MD_LEN - 1);
				for (int i = 0; i < t->used; i++) {
					m->dp[i] ^= t->dp[i];
				}
				m_len -= MD_LEN;
				bn_rsh(t, m, 8 * m_len);
				bn_write_bin(h2, MD_LEN, t);
				md_map(h1, NULL, 0);
				pad = 0;
				for (int i = 0; i < MD_LEN; i++) {
					pad |= h1[i] - h2[i];
				}
				if (result == STS_OK) {
					result = (pad ? STS_ERR : STS_OK);
				}
				bn_mod_2b(m, m, 8 * m_len);
				*p_len = bn_size_bin(m);
				(*p_len)--;
				bn_rsh(t, m, *p_len * 8);
				if (bn_cmp_dig(t, 1) != CMP_EQ) {
					result = STS_ERR;
				}
				bn_mod_2b(m, m, *p_len * 8);
				*p_len = k_len - *p_len;
				break;
			case RSA_SIG:
			case RSA_SIG_HASH:
				/* M' = 00 00 00 00 00 00 00 00 | H(M). */
				bn_zero(m);
				bn_lsh(m, m, 64);
				/* Make room for the real message. */
				bn_lsh(m, m, MD_LEN * 8);
				break;
			case RSA_SIG_FIN:
				memset(mask, 0, 8);
				bn_write_bin(mask + 8, MD_LEN, m);
				md_map(h1, mask, MD_LEN + 8);
				bn_read_bin(m, h1, MD_LEN);
				md_mgf1(mask, k_len - MD_LEN - 1, h1, MD_LEN);
				bn_read_bin(t, mask, k_len - MD_LEN - 1);
				t->dp[0] ^= 0x01;
				/* m_len is now the size in bits of the modulus. */
				bn_lsh(t, t, 8 * MD_LEN);
				bn_add(m, t, m);
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, RSA_PSS);
				for (int i = m_len - 1; i < 8 * k_len; i++) {
					bn_set_bit(m, i, 0);
				}
				break;
			case RSA_VER:
			case RSA_VER_HASH:
				bn_mod_2b(t, m, 8);
				if (bn_cmp_dig(t, RSA_PSS) != CMP_EQ) {
					result = STS_ERR;
				} else {
					for (int i = m_len; i < 8 * k_len; i++) {
						if (bn_get_bit(m, i) != 0) {
							result = STS_ERR;
						}
					}
					bn_rsh(m, m, 8);
					bn_mod_2b(t, m, 8 * MD_LEN);
					bn_write_bin(h2, MD_LEN, t);
					bn_rsh(m, m, 8 * MD_LEN);
					bn_write_bin(h1, MD_LEN, t);
					md_mgf1(mask, k_len - MD_LEN - 1, h1, MD_LEN);
					bn_read_bin(t, mask, k_len - MD_LEN - 1);
					for (int i = 0; i < t->used; i++) {
						m->dp[i] ^= t->dp[i];
					}
					m->dp[0] ^= 0x01;
					for (int i = m_len - 1; i < 8 * k_len; i++) {
						bn_set_bit(m, i - ((MD_LEN + 1) * 8), 0);
					}
					if (!bn_is_zero(m)) {
						result = STS_ERR;
					}
					bn_read_bin(m, h2, MD_LEN);
					*p_len = k_len - MD_LEN;
				}
				break;
		}
	}
	CATCH_ANY {
		result = STS_ERR;
	}
	FINALLY {
		bn_free(t);
	}

	return result;
}
示例#5
0
/**
 * Applies or removes a PKCS#1 v1.5 encryption padding.
 *
 * @param[out] m		- the buffer to pad.
 * @param[out] p_len	- the number of added pad bytes.
 * @param[in] m_len		- the message length in bytes.
 * @param[in] k_len		- the key length in bytes.
 * @param[in] operation	- flag to indicate the operation type.
 * @return STS_ERR if errors occurred, STS_OK otherwise.
 */
static int pad_pkcs1(bn_t m, int *p_len, int m_len, int k_len, int operation) {
	uint8_t *id, pad = 0;
	int len, result = STS_OK;
	bn_t t;

	bn_null(t);

	TRY {
		bn_new(t);

		switch (operation) {
			case RSA_ENC:
				/* EB = 00 | 02 | PS | 00 | D. */
				bn_zero(m);
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, RSA_PUB);

				*p_len = k_len - 3 - m_len;
				for (int i = 0; i < *p_len; i++) {
					bn_lsh(m, m, 8);
					do {
						rand_bytes(&pad, 1);
					} while (pad == 0);
					bn_add_dig(m, m, pad);
				}
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, 0);
				/* Make room for the real message. */
				bn_lsh(m, m, m_len * 8);
				break;
			case RSA_DEC:
				m_len = k_len - 1;
				bn_rsh(t, m, 8 * m_len);
				if (!bn_is_zero(t)) {
					result = STS_ERR;
				}

				*p_len = m_len;
				m_len--;
				bn_rsh(t, m, 8 * m_len);
				pad = (uint8_t)t->dp[0];
				if (pad != RSA_PUB) {
					result = STS_ERR;
				}
				do {
					m_len--;
					bn_rsh(t, m, 8 * m_len);
					pad = (uint8_t)t->dp[0];
				} while (pad != 0);
				/* Remove padding and trailing zero. */
				*p_len -= (m_len - 1);
				bn_mod_2b(m, m, (k_len - *p_len) * 8);
				break;
			case RSA_SIG:
				/* EB = 00 | 01 | PS | 00 | D. */
				id = hash_id(MD_MAP, &len);
				bn_zero(m);
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, RSA_PRV);

				*p_len = k_len - 3 - m_len - len;
				for (int i = 0; i < *p_len; i++) {
					bn_lsh(m, m, 8);
					bn_add_dig(m, m, RSA_PAD);
				}
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, 0);
				bn_lsh(m, m, 8 * len);
				bn_read_bin(t, id, len);
				bn_add(m, m, t);
				/* Make room for the real message. */
				bn_lsh(m, m, m_len * 8);
				break;
			case RSA_SIG_HASH:
				/* EB = 00 | 01 | PS | 00 | D. */
				bn_zero(m);
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, RSA_PRV);

				*p_len = k_len - 3 - m_len;
				for (int i = 0; i < *p_len; i++) {
					bn_lsh(m, m, 8);
					bn_add_dig(m, m, RSA_PAD);
				}
				bn_lsh(m, m, 8);
				bn_add_dig(m, m, 0);
				/* Make room for the real message. */
				bn_lsh(m, m, m_len * 8);
				break;
			case RSA_VER:
				m_len = k_len - 1;
				bn_rsh(t, m, 8 * m_len);
				if (!bn_is_zero(t)) {
					result = STS_ERR;
				}
				m_len--;
				bn_rsh(t, m, 8 * m_len);
				pad = (uint8_t)t->dp[0];
				if (pad != RSA_PRV) {
					result = STS_ERR;
				}
				do {
					m_len--;
					bn_rsh(t, m, 8 * m_len);
					pad = (uint8_t)t->dp[0];
				} while (pad != 0 && m_len > 0);
				if (m_len == 0) {
					result = STS_ERR;
				}
				/* Remove padding and trailing zero. */
				id = hash_id(MD_MAP, &len);
				m_len -= len;

				bn_rsh(t, m, m_len * 8);
				int r = 0;
				for (int i = 0; i < len; i++) {
					pad = (uint8_t)t->dp[0];
					r |= pad - id[len - i - 1];
					bn_rsh(t, t, 8);
				}
				*p_len = k_len - m_len;
				bn_mod_2b(m, m, m_len * 8);
				result = (r == 0 ? STS_OK : STS_ERR);
				break;
			case RSA_VER_HASH:
				m_len = k_len - 1;
				bn_rsh(t, m, 8 * m_len);
				if (!bn_is_zero(t)) {
					result = STS_ERR;
				}
				m_len--;
				bn_rsh(t, m, 8 * m_len);
				pad = (uint8_t)t->dp[0];
				if (pad != RSA_PRV) {
					result = STS_ERR;
				}
				do {
					m_len--;
					bn_rsh(t, m, 8 * m_len);
					pad = (uint8_t)t->dp[0];
				} while (pad != 0 && m_len > 0);
				if (m_len == 0) {
					result = STS_ERR;
				}
				/* Remove padding and trailing zero. */
				*p_len = k_len - m_len;
				bn_mod_2b(m, m, m_len * 8);
				break;
		}
	}
	CATCH_ANY {
		result = STS_ERR;
	}
	FINALLY {
		bn_free(t);
	}
	return result;
}
示例#6
0
int cp_ecss_ver(bn_t e, bn_t s, uint8_t *msg, int len, ec_t q) {
	bn_t n, ev, rv;
	ec_t p;
	uint8_t hash[MD_LEN];
	uint8_t m[len + FC_BYTES];
	int result = 0;

	bn_null(n);
	bn_null(ev);
	bn_null(rv);
	ec_null(p);

	TRY {
		bn_new(n);
		bn_new(ev);
		bn_new(rv);
		ec_new(p);

		ec_curve_get_ord(n);

		if (bn_sign(e) == BN_POS && bn_sign(s) == BN_POS && !bn_is_zero(s)) {
			if (bn_cmp(e, n) == CMP_LT && bn_cmp(s, n) == CMP_LT) {
				ec_mul_sim_gen(p, s, q, e);
				ec_get_x(rv, p);

				bn_mod(rv, rv, n);

				memcpy(m, msg, len);
				bn_write_bin(m + len, FC_BYTES, rv);
				md_map(hash, m, len + FC_BYTES);

				if (8 * MD_LEN > bn_bits(n)) {
					len = CEIL(bn_bits(n), 8);
					bn_read_bin(ev, hash, len);
					bn_rsh(ev, ev, 8 * MD_LEN - bn_bits(n));
				} else {
					bn_read_bin(ev, hash, MD_LEN);
				}

				bn_mod(ev, ev, n);

				result = dv_cmp_const(ev->dp, e->dp, MIN(ev->used, e->used));
				result = (result == CMP_NE ? 0 : 1);

				if (ev->used != e->used) {
					result = 0;
				}
			}
		}
	}
	CATCH_ANY {
		THROW(ERR_CAUGHT);
	}
	FINALLY {
		bn_free(n);
		bn_free(ev);
		bn_free(rv);
		ec_free(p);
	}
	return result;
}
int bn_is_prime_solov(const bn_t a) {
	bn_t t0, t1, t2;
	int i, result;

	bn_null(t0);
	bn_null(t1);
	bn_null(t2);

	result = 1;

	TRY {
		bn_new(t0);
		bn_new(t1);
		bn_new(t2);

		for (i = 0; i < 100; i++) {
			/* Generate t0, 2 <= t0, <= a - 2. */
			do {
				bn_rand(t0, BN_POS, bn_bits(a));
				bn_mod(t0, t0, a);
			} while (bn_cmp_dig(t0, 2) == CMP_LT);
			/* t2 = a - 1. */
			bn_copy(t2, a);
			bn_sub_dig(t2, t2, 1);
			/* t1 = (a - 1)/2. */
			bn_rsh(t1, t2, 1);
			/* t1 = t0^(a - 1)/2 mod a. */
#if BN_MOD != PMERS
			bn_mxp(t1, t0, t1, a);
#else
			bn_exp(t1, t0, t1, a);
#endif
			/* If t1 != 1 and t1 != n - 1 return 0 */
			if (bn_cmp_dig(t1, 1) != CMP_EQ && bn_cmp(t1, t2) != CMP_EQ) {
				result = 0;
				break;
			}

			/* t2 = (t0|a). */
			bn_smb_jac(t2, t0, a);
			if (bn_sign(t2) == BN_NEG) {
				bn_add(t2, t2, a);
			}
			/* If t1 != t2 (mod a) return 0. */
			bn_mod(t1, t1, a);
			bn_mod(t2, t2, a);
			if (bn_cmp(t1, t2) != CMP_EQ) {
				result = 0;
				break;
			}
		}
	}
	CATCH_ANY {
		result = 0;
		THROW(ERR_CAUGHT);
	}
	FINALLY {
		bn_free(t0);
		bn_free(t1);
		bn_free(t2);
	}
	return result;
}
int bn_is_prime_rabin(const bn_t a) {
	bn_t t, n1, y, r;
	int i, s, j, result, b, tests = 0;

	tests = 0;
	result = 1;

	bn_null(t);
	bn_null(n1);
	bn_null(y);
	bn_null(r);

	if (bn_cmp_dig(a, 1) == CMP_EQ) {
		return 0;
	}

	TRY {
		/*
		 * These values are taken from Table 4.4 inside Handbook of Applied
		 * Cryptography.
		 */
		b = bn_bits(a);
		if (b >= 1300) {
			tests = 2;
		} else if (b >= 850) {
			tests = 3;
		} else if (b >= 650) {
			tests = 4;
		} else if (b >= 550) {
			tests = 5;
		} else if (b >= 450) {
			tests = 6;
		} else if (b >= 400) {
			tests = 7;
		} else if (b >= 350) {
			tests = 8;
		} else if (b >= 300) {
			tests = 9;
		} else if (b >= 250) {
			tests = 12;
		} else if (b >= 200) {
			tests = 15;
		} else if (b >= 150) {
			tests = 18;
		} else {
			tests = 27;
		}

		bn_new(t);
		bn_new(n1);
		bn_new(y);
		bn_new(r);

		/* r = (n - 1)/2^s. */
		bn_sub_dig(n1, a, 1);
		s = 0;
		while (bn_is_even(n1)) {
			s++;
			bn_rsh(n1, n1, 1);
		}
		bn_lsh(r, n1, s);

		for (i = 0; i < tests; i++) {
			/* Fix the basis as the first few primes. */
			bn_set_dig(t, primes[i]);

			/* y = b^r mod a. */
#if BN_MOD != PMERS
			bn_mxp(y, t, r, a);
#else
			bn_exp(y, t, r, a);
#endif

			if (bn_cmp_dig(y, 1) != CMP_EQ && bn_cmp(y, n1) != CMP_EQ) {
				j = 1;
				while ((j <= (s - 1)) && bn_cmp(y, n1) != CMP_EQ) {
					bn_sqr(y, y);
					bn_mod(y, y, a);

					/* If y == 1 then composite. */
					if (bn_cmp_dig(y, 1) == CMP_EQ) {
						result = 0;
						break;
					}
					++j;
				}

				/* If y != n1 then composite. */
				if (bn_cmp(y, n1) != CMP_EQ) {
					result = 0;
					break;
				}
			}
		}
	}
	CATCH_ANY {
		result = 0;
		THROW(ERR_CAUGHT);
	}
	FINALLY {
		bn_free(r);
		bn_free(y);
		bn_free(n1);
		bn_free(t);
	}
	return result;
}
示例#9
0
void cp_ecss_sig(bn_t e, bn_t s, unsigned char *msg, int len, bn_t d) {
	bn_t n, k, x, r;
	ec_t p;
	unsigned char hash[MD_LEN];
	unsigned char m[len + EC_BYTES];

	bn_null(n);
	bn_null(k);
	bn_null(x);
	bn_null(r);
	ec_null(p);

	TRY {
		bn_new(n);
		bn_new(k);
		bn_new(x);
		bn_new(r);
		ec_new(p);

		ec_curve_get_ord(n);
		do {
			do {
				bn_rand(k, BN_POS, bn_bits(n));
				bn_mod(k, k, n);
			} while (bn_is_zero(k));

			ec_mul_gen(p, k);
			ec_get_x(x, p);
			bn_mod(r, x, n);
		} while (bn_is_zero(r));

		memcpy(m, msg, len);
		bn_write_bin(m + len, EC_BYTES, r);
		md_map(hash, m, len + EC_BYTES);

		if (8 * MD_LEN > bn_bits(n)) {
			len = CEIL(bn_bits(n), 8);
			bn_read_bin(e, hash, len);
			bn_rsh(e, e, 8 * MD_LEN - bn_bits(n));
		} else {
			bn_read_bin(e, hash, MD_LEN);
		}

		bn_mod(e, e, n);

		bn_mul(s, d, e);
		bn_mod(s, s, n);
		bn_sub(s, n, s);
		bn_add(s, s, k);
		bn_mod(s, s, n);
	}
	CATCH_ANY {
		THROW(ERR_CAUGHT);
	}
	FINALLY {
		bn_free(n);
		bn_free(k);
		bn_free(x);
		bn_free(r);
		ec_free(p);
	}
}
示例#10
0
int fp_srt(fp_t c, const fp_t a) {
	bn_t e;
	fp_t t0;
	fp_t t1;
	int r = 0;

	bn_null(e);
	fp_null(t0);
	fp_null(t1);

	TRY {
		bn_new(e);
		fp_new(t0);
		fp_new(t1);

		/* Make e = p. */
		e->used = FP_DIGS;
		dv_copy(e->dp, fp_prime_get(), FP_DIGS);

		if (fp_prime_get_mod8() == 3 || fp_prime_get_mod8() == 7) {
			/* Easy case, compute a^((p + 1)/4). */
			bn_add_dig(e, e, 1);
			bn_rsh(e, e, 2);

			fp_exp(t0, a, e);
			fp_sqr(t1, t0);
			r = (fp_cmp(t1, a) == CMP_EQ);
			fp_copy(c, t0);
		} else {
			int f = 0, m = 0;

			/* First, check if there is a root. Compute t1 = a^((p - 1)/2). */
			bn_rsh(e, e, 1);
			fp_exp(t0, a, e);

			if (fp_cmp_dig(t0, 1) != CMP_EQ) {
				/* Nope, there is no square root. */
				r = 0;
			} else {
				r = 1;
				/* Find a quadratic non-residue modulo p, that is a number t2
				 * such that (t2 | p) = t2^((p - 1)/2)!= 1. */
				do {
					fp_rand(t1);
					fp_exp(t0, t1, e);
				} while (fp_cmp_dig(t0, 1) == CMP_EQ);

				/* Write p - 1 as (e * 2^f), odd e. */
				bn_lsh(e, e, 1);
				while (bn_is_even(e)) {
					bn_rsh(e, e, 1);
					f++;
				}

				/* Compute t2 = t2^e. */
				fp_exp(t1, t1, e);

				/* Compute t1 = a^e, c = a^((e + 1)/2) = a^(e/2 + 1), odd e. */
				bn_rsh(e, e, 1);
				fp_exp(t0, a, e);
				fp_mul(e->dp, t0, a);
				fp_sqr(t0, t0);
				fp_mul(t0, t0, a);
				fp_copy(c, e->dp);

				while (1) {
					if (fp_cmp_dig(t0, 1) == CMP_EQ) {
						break;
					}
					fp_copy(e->dp, t0);
					for (m = 0; (m < f) && (fp_cmp_dig(t0, 1) != CMP_EQ); m++) {
						fp_sqr(t0, t0);
					}
					fp_copy(t0, e->dp);
					for (int i = 0; i < f - m - 1; i++) {
						fp_sqr(t1, t1);
					}
					fp_mul(c, c, t1);
					fp_sqr(t1, t1);
					fp_mul(t0, t0, t1);
					f = m;
				}
			}
		}
	}
	CATCH_ANY {
		THROW(ERR_CAUGHT);
	}
	FINALLY {
		bn_free(e);
		fp_free(t0);
		fp_free(t1);
	}
	return r;
}