aboutsummaryrefslogtreecommitdiff
/*
 * SPDX-License-Identifier: CC0-1.0
 *
 * Copyright (C) 2025 W. Kosior <koszko@koszko.org>
 */

#define _POSIX_C_SOURCE 200809L /* for fmemopen() */

#include <arpa/inet.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "pqcrypto_bitcnt_bytes.h"
#define PQCRYPTO_BLIND_SIG_C
#include "pqcrypto_blind_sig.h"
#include "pqcrypto_commitment_shake256.h"
#include "pqcrypto_hash_shake256.h"
#include "pqcrypto_prng_getrandom.h"
#include "pqcrypto_prng_seeded.h"



/***
 *** Serialization, deserialization and deinitialization of structures.
 ***/

/*
 * bool (de)serialization.
 *
 * Note: these 2 function rely on additionam macro wrappers for them (defined
 * later on) and they don't accept the context argument.
 */

static size_t bool_serialize(FILE * stream, bool flag) {
	if (putc(flag, stream) == EOF)
		abort();

	return 1;
}

/*
 * Note: this deserialization function accepts destination pointer not concealed
 * as an array type.
 */

static size_t bool_deserialize(FILE * stream, bool * flag) {
	int result = getc(stream);

	if (result == EOF)
		abort();

	*flag = result;

	return 1;
}

/*
 * Reasonably big number (de)serialization.
 *
 * Note: these 2 functions are only called "manually" (not from
 * SERIALIZABLE_STRUCT() macros) and they don't accept the context argument.
 */

static size_t uint_least64_serialize(FILE * stream, uint_least64_t number) {
	uint32_t halfs[2];

	halfs[0] = htonl(number >> 32);
	halfs[1] = htonl(number & UINT32_MAX);

	if (fwrite(halfs, sizeof(halfs), 1, stream) != 1)
		abort();

	return sizeof(halfs);
}

/*
 * Note: this deserialization function accepts destination pointer not concealed
 * as an array type.
 */

static size_t uint_least64_deserialize(FILE * stream, uint_least64_t * number) {
	uint32_t halfs[2];

	if (fread(halfs, sizeof(halfs), 1, stream) != 1)
		abort();

	*number = ((uint_least64_t) ntohl(halfs[0])) << 32;
	*number |= ntohl(halfs[1]);

	return sizeof(halfs);
}

/* fmpz_t (de)serialization and deinitialization. */

static size_t fmpz_t_serialize(FILE * stream, fmpz_t const number,
			       blind_sig_ctx_t const ctx) {
	size_t written = fmpz_out_raw(stream, number);

	(void) ctx;

	if (!written)
		abort();

	return written;
}

static size_t fmpz_t_deserialize(FILE * stream, fmpz_t number,
				 blind_sig_ctx_t const ctx) {
	size_t read;

	(void) ctx;

	fmpz_init(number);

	read = fmpz_inp_raw(number, stream);
	if (!read)
		abort();

	return read;
}

inline static void fmpz_t_clear(fmpz_t fmpz, blind_sig_ctx_t const ctx) {
	(void) ctx;

	fmpz_clear(fmpz);
}

/* Message (de)serialization and deinitialization. */

static size_t blind_sig_message_t_serialize(FILE * stream,
					    blind_sig_message_t const message,
					    blind_sig_ctx_t const ctx) {
	size_t written;

	(void) ctx;

	written = uint_least64_serialize(stream, message->bytes);

	if (fwrite(message->buf, message->bytes, 1, stream) != 1)
		abort();

	written += message->bytes;

	return written;
}

static size_t blind_sig_message_t_deserialize(FILE * stream,
					      blind_sig_message_t message,
					      blind_sig_ctx_t const ctx) {
	uint_least64_t size;
	size_t read;

	(void) ctx;

	read = uint_least64_deserialize(stream, &size);
	if (size > SIZE_MAX)
		abort();

	message->bytes = size;

	message->buf = malloc(size);
	if (!message->buf)
		abort();

	if (fread(message->buf, size, 1, stream) != 1)
		abort();

	read += size;

	return read;
}

inline static void blind_sig_message_t_clear(blind_sig_message_t message,
					     blind_sig_ctx_t const ctx) {
	(void) ctx;

	free(message->buf);
}

/* fmpz_poly_t (de)serialization. */

static size_t fmpz_poly_t_serialize(FILE * stream, fmpz_poly_t const poly,
				    blind_sig_ctx_t const ctx) {
	slong length = fmpz_poly_length(poly);
	fmpz_t coef;
	size_t written;

	written = uint_least64_serialize(stream, length);

	fmpz_init(coef);

	while (length--) {
		fmpz_poly_get_coeff_fmpz(coef, poly, length);
		written += fmpz_t_serialize(stream, coef, ctx);
	}

	fmpz_clear(coef);

	return written;
}

static size_t fmpz_poly_t_deserialize(FILE * stream, fmpz_poly_t poly,
				      blind_sig_ctx_t const ctx) {
	uint_least64_t length;
	fmpz_t coef;
	size_t read;

	read = uint_least64_deserialize(stream, &length);
	if (length > WORD_MAX)
		abort();

	fmpz_poly_init(poly);

	while (length && length--) {
		read += fmpz_t_deserialize(stream, coef, ctx);
		fmpz_poly_set_coeff_fmpz(poly, length, coef);
		fmpz_clear(coef);
	}

	return read;
}

inline static void fmpz_poly_t_clear(fmpz_poly_t poly,
				     blind_sig_ctx_t const ctx) {
	(void) ctx;

	fmpz_poly_clear(poly);
}

/* Polynomials vector (de)serialization. */

static size_t blind_sig_poly_vector_t_serialize(FILE * stream,
						blind_sig_poly_vector_t const
						polys,
						blind_sig_ctx_t const ctx) {
	size_t written = 0;

	for (ulong i = 0; i < ctx->m; i++)
		written += fmpz_poly_t_serialize(stream, *polys + i, ctx);

	return written;
}

static size_t blind_sig_poly_vector_t_deserialize(FILE * stream,
						  blind_sig_poly_vector_t
						  polys,
						  blind_sig_ctx_t const ctx) {
	size_t read = 0;

	*polys = malloc(ctx->m * sizeof(fmpz_poly_struct));
	if (!*polys)
		abort();

	for (ulong i = 0; i < ctx->m; i++)
		read += fmpz_poly_t_deserialize(stream, *polys + i, ctx);

	return read;
}

static void blind_sig_poly_vector_t_clear(blind_sig_poly_vector_t polys,
					  blind_sig_ctx_t const ctx) {
	for (ulong i = 0; i < ctx->m; i++)
		fmpz_poly_clear(*polys + i);

	free(*polys);
}

/* Randomness or commitment buffer (de)serialization. */

static size_t blind_sig_n_bit_buf_t_serialize(FILE * stream,
					      blind_sig_n_bit_buf_t const
					      buffer,
					      blind_sig_ctx_t const ctx) {
	size_t bytes = BITCNT_BYTES(ctx->n);

	if (fwrite(*buffer, bytes, 1, stream) != 1)
		abort();

	return bytes;
}

static size_t blind_sig_n_bit_buf_t_deserialize(FILE * stream,
						blind_sig_n_bit_buf_t buffer,
						blind_sig_ctx_t const ctx) {
	size_t bytes = BITCNT_BYTES(ctx->n);

	*buffer = malloc(bytes);
	if (!*buffer)
		abort();

	if (fread(*buffer, bytes, 1, stream) != 1)
		abort();

	return bytes;
}

inline static void blind_sig_n_bit_buf_t_clear(blind_sig_n_bit_buf_t buffer,
					       blind_sig_ctx_t const ctx) {
	(void) ctx;

	free(*buffer);
}

/*
 * bool (de)serializer macro wrappers that returns early if bool field's value
 * is false.
 *
 * Here we assume the bool is always an ok-type flag and later struct members
 * are only valid/initialized/meaningful when it is true.
 */

#define _Bool_serialize(stream, bool_expr, ctx)				\
	/* count += */ bool_serialize(stream, bool_expr);		\
									\
	if (!bool_expr)							\
		return count

#define _Bool_deserialize(stream, bool_expr, ctx)			\
	/* count += */ bool_deserialize(stream, &bool_expr);		\
									\
	if (!bool_expr)							\
		return count

/* (De)serialization of structures from the separate file. */

#define FIELD(type, name)						\
	count += MAKE_NAME(type)(stream, obj->name, ctx);

#define SERIALIZABLE_STRUCT(name, fields)				\
	size_t MAKE_NAME(name)(FILE * stream, name##_t MAYBE_CONST obj,	\
			       blind_sig_ctx_t const ctx) {		\
		size_t count = 0;					\
									\
		fields;							\
									\
		return count;						\
	}								\
									\
	void MAKE_NAME(name##_file)(const char * filename,		\
				    name##_t MAYBE_CONST obj,		\
				    blind_sig_ctx_t const ctx) {	\
		FILE * stream = fopen(filename, MAKE_NAME(FOPEN_MODE));	\
									\
		if (!stream)						\
			abort();					\
									\
		MAKE_NAME(name)(stream, obj, ctx);			\
									\
		if (fclose(stream) == EOF)				\
			abort();					\
	}

#define FOPEN_MODE_serialize "w"
#define FOPEN_MODE_deserialize "r"

#define MAKE_NAME(prefix) prefix##_serialize
#define MAYBE_CONST const

#include "pqcrypto_blind_sig_serializable.c"

#undef MAKE_NAME
#undef MAYBE_CONST

#define MAKE_NAME(prefix) prefix##_deserialize
#define MAYBE_CONST

#include "pqcrypto_blind_sig_serializable.c"

#undef MAKE_NAME
#undef MAYBE_CONST

#undef FOPEN_MODE_serialize
#undef FOPEN_MODE_deserialize

#undef FIELD
#undef SERIALIZABLE_STRUCT

#undef bool_serialize
#undef bool_deserialize

/* bool deinitializer macro (similar logic as before). */

#define bool_clear(bool_expr, ctx)					\
	if (!bool_expr)							\
		return;

/* Deinitialization of structures from the separate file. */

#define FIELD(type, name)			\
	type##_clear(obj->name, ctx);

#define SERIALIZABLE_STRUCT(name, fields)				\
	void name##_clear(name##_t obj, blind_sig_ctx_t const ctx) {	\
		fields;							\
	}

#include "pqcrypto_blind_sig_serializable.c"

#undef FIELD
#undef SERIALIZABLE_STRUCT

#undef bool_clear



/***
 *** Initialization and operations on blind_sig_message_t,
 *** blind_sig_poly_vector_t and blind_sig_n_bit_buf_t.
 ***/

/* Initialization of blind_sig_message_t. */

static void blind_sig_message_init_set_buf(blind_sig_message_t message,
					   void const * buf, size_t bytes) {
	message->buf = malloc(bytes);
	if (!message->buf)
		abort();

	memcpy(message->buf, buf, bytes);
	message->bytes = bytes;
}

/* Initialization of blind_sig_poly_vector_t. */

static void blind_sig_poly_vector_init(blind_sig_poly_vector_t polys,
				       blind_sig_ctx_t const ctx) {
	*polys = malloc(ctx->m * sizeof(fmpz_poly_struct));
	if (!*polys)
		abort();

	for (ulong i = 0; i < ctx->m; i++)
		fmpz_poly_init(*polys + i);
}

static void blind_sig_poly_vector_init_set(blind_sig_poly_vector_t dst_polys,
					   blind_sig_poly_vector_t const
					   src_polys,
					   blind_sig_ctx_t const ctx) {
	blind_sig_poly_vector_init(dst_polys, ctx);

	for (ulong i = 0; i < ctx->m; i++)
		fmpz_poly_set(*dst_polys + i, *src_polys + i);
}

/* Initialization of blind_sig_n_bit_buf_t. */

static void blind_sig_n_bit_buf_init(blind_sig_n_bit_buf_t n_bit_buf,
				     blind_sig_ctx_t const ctx) {
	*n_bit_buf = malloc(BITCNT_BYTES(ctx->n));
	if (!*n_bit_buf)
		abort();
}

static void blind_sig_n_bit_buf_init_set(blind_sig_n_bit_buf_t dst_n_bit_buf,
					 blind_sig_n_bit_buf_t const
					 src_n_bit_buf,
					 blind_sig_ctx_t const ctx) {
	blind_sig_n_bit_buf_init(dst_n_bit_buf, ctx);

	memcpy(*dst_n_bit_buf, *src_n_bit_buf, BITCNT_BYTES(ctx->n));
}

/***
 *** Context handling.
 ***/

blind_sig_params_t const blind_sig_params_current_III = {
	{
		.n = 1024,
		.q_bits = 81,
		.phi = 4,
		.psi = 1,
		.d_s = 283,
		.m = 9,
		.log_level = BLIND_SIG_LOG_ERROR,
	}
};

blind_sig_params_t const blind_sig_params_toy = {
	{
		.n = 4,
		.q_bits = 17,
		.phi = 2,
		.psi = 1,
		.d_s = 3,
		.m = 6,
		.log_level = BLIND_SIG_LOG_DEBUG_2_VALUES,
	}
};

static char const public_initialization_seed[] = "Ruckert 2008 lattices";

void blind_sig_ctx_init(blind_sig_ctx_t ctx, blind_sig_params_t const params) {
	prng_seeded_state_t prng_state;
	fmpz_t q, d_s_check, mod_divisor_tmp;
	mod_centered_0_ctx_t mod_ctx;

	/* Make some sanity checks. */

	if (params->q_bits < 3 ||
	    params->q_bits > UWORD_MAX ||
	    params->n < 2 ||
	    params->n > WORD_MAX ||
	    BITCNT_BYTES(params->n) > SIZE_MAX ||
	    params->d_s < 1 ||
	    params->d_s > WORD_MAX ||
	    SIZE_MAX / sizeof(fmpz_poly_struct) < params->m)
		abort();

	/* Set the simple params. */

	ctx->n = params->n;
	ctx->d_s = params->d_s;
	ctx->m = params->m;
	ctx->phi = params->phi;
	ctx->psi = params->psi;
	ctx->public_commitment_function = commitment_shake256;
	*ctx->public_hash_function = *hash_function_shake256;
	ctx->log_level = params->log_level;

	/* Prepare this fmpz for use throughout this function. */

	fmpz_init(mod_divisor_tmp);

	/* Derive the prime for modulo operations. */

	fmpz_init(q);
	fmpz_ui_pow_ui(q, 2, params->q_bits - 1);
	fmpz_nextprime(q, q, 0);

	/* Make sure d_s is not too big. */

	fmpz_init_set_ui(d_s_check, params->d_s);
	fmpz_mul2_uiui(d_s_check, d_s_check, params->n, 4);

	if (fmpz_cmp(d_s_check, q) >= 0)
		abort();

	fmpz_clear(d_s_check);

	/* Prepare the context for key generation. */

	if (params->d_s > (UWORD_MAX - 1) / 2)
		abort();

	fmpz_set_ui(mod_divisor_tmp, params->d_s * 2 + 1);

	mod_c0_ctx_init(ctx->key_gen_ctx, mod_divisor_tmp);

	/* Prepare d_alpha and the context for generating the alpha value. */

	if (UWORD_MAX / params->psi < params->n)
		abort();

	ctx->d_alpha = params->psi * params->n;

	fmpz_set_ui(mod_divisor_tmp, ctx->d_alpha);
	fmpz_mul_2exp(mod_divisor_tmp, mod_divisor_tmp, 1);
	fmpz_add_ui(mod_divisor_tmp, mod_divisor_tmp, 1);

	mod_c0_ctx_init(ctx->alpha_gen_ctx, mod_divisor_tmp);

	/*
	 * Prepare d_epsilon_star and d_y, as well as the context for generating
	 * the Y commitment.
	 */

	ctx->d_epsilon_star = ctx->d_alpha - 1;

	fmpz_init(ctx->d_y);
	fmpz_ui_pow_ui(ctx->d_y, params->n, 2);
	fmpz_mul_ui(ctx->d_y, ctx->d_y, params->phi);
	fmpz_mul_ui(ctx->d_y, ctx->d_y, params->m);
	fmpz_mul_ui(ctx->d_y, ctx->d_y, params->d_s);
	fmpz_mul_ui(ctx->d_y, ctx->d_y, ctx->d_epsilon_star);

	fmpz_mul_2exp(mod_divisor_tmp, ctx->d_y, 1);
	fmpz_add_ui(mod_divisor_tmp, mod_divisor_tmp, 1);

	mod_c0_ctx_init(ctx->y_commitment_gen_ctx, mod_divisor_tmp);

	/*
	 * Prepare d_g_star, d_beta and d_g, as well as the context for
	 * generating the Beta.
	 */

	fmpz_init_set_ui(ctx->d_g_star, params->n);
	fmpz_mul_ui(ctx->d_g_star, ctx->d_g_star, params->d_s);
	fmpz_mul_ui(ctx->d_g_star, ctx->d_g_star, ctx->d_epsilon_star);
	fmpz_sub(ctx->d_g_star, ctx->d_y, ctx->d_g_star);

	fmpz_init_set_ui(ctx->d_beta, params->phi);
	fmpz_mul_ui(ctx->d_beta, ctx->d_beta, params->m);
	fmpz_mul_ui(ctx->d_beta, ctx->d_beta, params->n);
	fmpz_mul(ctx->d_beta, ctx->d_beta, ctx->d_g_star);

	fmpz_init(ctx->d_g);
	fmpz_sub(ctx->d_g, ctx->d_beta, ctx->d_g_star);

	fmpz_mul_2exp(mod_divisor_tmp, ctx->d_beta, 1);
	fmpz_add_ui(mod_divisor_tmp, mod_divisor_tmp, 1);

	mod_c0_ctx_init(ctx->beta_gen_ctx, mod_divisor_tmp);

	/* Prepare contexts for modulo field and polynomial operations. */

	mod_c0_ctx_init(mod_ctx, q);

	poly_ring_ctx_init(ctx->poly_ring_ctx, mod_ctx, params->n);

	mod_c0_ctx_clear(mod_ctx);

	/* Derive the vector of polynomials to serve as public homomorphism. */

	blind_sig_poly_vector_init(ctx->public_homomorphism, ctx);

	prng_seeded_state_init(prng_state, public_initialization_seed,
			       sizeof(public_initialization_seed));

	for (ulong i = 0; i < params->m; i++) {
		poly_prng_in_ring(*ctx->public_homomorphism + i,
				  prng_seeded, prng_state, ctx->poly_ring_ctx);
	}

	prng_seeded_state_clear(prng_state);

	/* Print parameters. */

#define X(fmt, arg) blind_sig_log_INFO_2_PARAMETERS(ctx, fmt, arg)

	X("n = %{ulong}\n", ctx->n);
	X("d_s = %{ulong}\n", ctx->d_s);
	X("m = %{ulong}\n", ctx->m);
	X("phi = %{ulong}\n", ctx->phi);
	X("psi = %{ulong}\n", ctx->psi);
	X("d_alpha = %{ulong}\n", ctx->d_alpha);
	X("d_epsilon_star = %{ulong}\n", ctx->d_epsilon_star);
	X("d_y = %{fmpz}\n", ctx->d_y);
	X("d_g_star = %{fmpz}\n", ctx->d_g_star);
	X("d_beta = %{fmpz}\n", ctx->d_beta);
	X("d_g = %{fmpz}\n", ctx->d_g);
	X("q = %{fmpz}\n", q);

#undef X

	/* Free the memory associated temprarily. */

	fmpz_clear(mod_divisor_tmp);
	fmpz_clear(q);
}

void blind_sig_ctx_clear(blind_sig_ctx_t ctx) {
	mod_c0_ctx_clear(ctx->key_gen_ctx);

	mod_c0_ctx_clear(ctx->alpha_gen_ctx);

	fmpz_clear(ctx->d_y);

	mod_c0_ctx_clear(ctx->y_commitment_gen_ctx);

	fmpz_clear(ctx->d_g_star);

	fmpz_clear(ctx->d_beta);

	mod_c0_ctx_clear(ctx->beta_gen_ctx);

	fmpz_clear(ctx->d_g);

	poly_ring_ctx_clear(ctx->poly_ring_ctx);

	blind_sig_poly_vector_t_clear(ctx->public_homomorphism, ctx);
}



/***
 *** Helper functions used from a few places.
 ***/

int blind_sig_vlog(blind_sig_ctx_t const ctx,
		   enum blind_sig_log_level message_level,
		   char const * fmt, va_list ap) {
	if (message_level <= ctx->log_level)
		return flint_vfprintf(stderr, fmt, ap);

	return 0;
}

static void apply_homomorphism(fmpz_poly_t res,
			       blind_sig_poly_vector_t const polys,
			       blind_sig_ctx_t const ctx) {
	fmpz_poly_t tmp;

	fmpz_poly_init(tmp);

	for (ulong i = 0; i < ctx->m; i++) {
		poly_mul_in_ring(i ? tmp : res,
				 *polys + i, *ctx->public_homomorphism + i,
				 ctx->poly_ring_ctx);

		if (i)
			poly_add_in_ring(res, res, tmp, ctx->poly_ring_ctx);
	}

	fmpz_poly_clear(tmp);
}

static void apply_hash(fmpz_poly_t res, fmpz_poly_t const input,
		       blind_sig_n_bit_buf_t const commitment,
		       blind_sig_ctx_t const ctx) {
	int extracted;
	void * hash_state = ctx->public_hash_function->make();
	void * fmpz_buf;
	FILE * fmpz_memfile;
	size_t fmpz_bytes, fmpz_written;
	fmpz_t coef;

	fmpz_bytes = fmpz_size(ctx->poly_ring_ctx->mod_ctx->divisor);
	/* Give space for 1 more limb just in case. */
	fmpz_bytes++;
	fmpz_bytes *= sizeof(ulong);
	/* Fit serialized length, which is 4 bytes according to Flint doc. */
	fmpz_bytes += 4;

	fmpz_buf = malloc(fmpz_bytes);
	if (!fmpz_buf)
		abort();

	fmpz_memfile = fmemopen(fmpz_buf, fmpz_bytes, "w");
	setbuf(fmpz_memfile, NULL);

	fmpz_init(coef);

	for (ulong i = 0; i < ctx->n; i++) {
		fmpz_poly_get_coeff_fmpz(coef, input, i);
		fmpz_written = fmpz_out_raw(fmpz_memfile, coef);
		if (!fmpz_written)
			abort();

		ctx->public_hash_function->feed(hash_state, fmpz_buf,
						fmpz_written);

		fseek(fmpz_memfile, 0, SEEK_SET);
	}

	ctx->public_hash_function->feed(hash_state, *commitment,
					BITCNT_BYTES(ctx->n));

	fmpz_poly_zero(res);
	fmpz_poly_realloc(res, ctx->n);

	for (ulong i = 0; i < ctx->n; i++) {
		do {
			extracted = ctx->public_hash_function->getc(hash_state);

			fmpz_poly_set_coeff_si(res, i, extracted % 3 - 1);
		} while (extracted >= 255);
	}

	ctx->public_hash_function->free(hash_state);

	fclose(fmpz_memfile);
	free(fmpz_buf);

	fmpz_clear(coef);
}



/***
 *** Key handling.
 ***/

void blind_sig_key_init_gen(blind_sig_priv_key_t priv_key,
			    blind_sig_pub_key_t pub_key,
			    blind_sig_ctx_t const ctx) {
	blind_sig_poly_vector_init(priv_key->key_polys, ctx);

	for (ulong i = 0; i < ctx->m; i++) {
		poly_rand_mod_c0(*priv_key->key_polys + i, ctx->n,
				 ctx->key_gen_ctx);
	}

	fmpz_poly_init(pub_key->key_poly);

	apply_homomorphism(pub_key->key_poly, priv_key->key_polys, ctx);
}



/***
 *** Signature protocol.
 ***/

void blind_sig_signer_state_init(blind_sig_signer_state_t state,
				 blind_sig_ctx_t const ctx) {
	blind_sig_poly_vector_init(state->y_commitment_random, ctx);
	fmpz_poly_init(state->y_commitment);
	blind_sig_poly_vector_init(state->z_star, ctx);
	fmpz_poly_init(state->epsilon_star);
}

void blind_sig_user_state_init(blind_sig_user_state_t state,
			       blind_sig_ctx_t const ctx) {
	blind_sig_n_bit_buf_init(state->randomness, ctx);
	blind_sig_n_bit_buf_init(state->commitment, ctx);
	fmpz_poly_init(state->alpha);
	blind_sig_poly_vector_init(state->beta, ctx);
	fmpz_poly_init(state->epsilon);
	fmpz_poly_init(state->epsilon_star);
}

void blind_sig_proto_p1_init_do(blind_sig_proto_p1_t result,
				blind_sig_signer_state_t state,
				blind_sig_ctx_t const ctx) {
	blind_sig_log_DEBUG_1_PHASES(ctx, "Protocol phase 1 started.\n");

	for (ulong i = 0; i < ctx->m; i++) {
		poly_rand_mod_c0(*state->y_commitment_random + i,
				 ctx->n, ctx->y_commitment_gen_ctx);
	}

	apply_homomorphism(state->y_commitment, state->y_commitment_random,
			   ctx);

	fmpz_poly_init(result->y_commitment);
	fmpz_poly_set(result->y_commitment, state->y_commitment);

	blind_sig_log_DEBUG_2_VALUES(
		ctx, "Y commitment computed as %{fmpz_poly}.\n",
		result->y_commitment);
}

void blind_sig_proto_p2_init_do(blind_sig_proto_p2_t result,
				blind_sig_user_state_t state,
				blind_sig_pub_key_t const pub_key,
				void const * message_buf,
				size_t message_bytes,
				blind_sig_proto_p1_t const p1_result,
				unsigned long * retries_left,
				blind_sig_ctx_t const ctx) {
	fmpz_poly_t transformed_beta;

	blind_sig_message_init_set_buf(state->message, message_buf,
				       message_bytes);

	blind_sig_log_DEBUG_1_PHASES(ctx, "Protocol phase 2 started.\n");

	prng_getrandom(*state->randomness, BITCNT_BYTES(ctx->n), NULL);

	ctx->public_commitment_function(
		*state->commitment, message_buf, message_bytes,
		*state->randomness, ctx->n);

	for (ulong i = 0; i < ctx->m; i++)
		poly_rand_mod_c0(*state->beta + i, ctx->n, ctx->beta_gen_ctx);

	fmpz_poly_init(transformed_beta);

	apply_homomorphism(transformed_beta, state->beta, ctx);

	while (true) {
		poly_rand_mod_c0(state->alpha, ctx->n, ctx->alpha_gen_ctx);

		blind_sig_log_DEBUG_2_VALUES(
			ctx, "alpha chosen as %{fmpz_poly}.\n", state->alpha);

		poly_mul_in_ring(state->epsilon, pub_key->key_poly,
				 state->alpha, ctx->poly_ring_ctx);
		fmpz_poly_neg(state->epsilon, state->epsilon);

		poly_add_in_ring(state->epsilon, state->epsilon,
				 p1_result->y_commitment, ctx->poly_ring_ctx);

		poly_sub_in_ring(state->epsilon, state->epsilon,
				 transformed_beta, ctx->poly_ring_ctx);

		apply_hash(state->epsilon, state->epsilon, state->commitment,
			   ctx);

		blind_sig_log_DEBUG_2_VALUES(
			ctx, "epsilon computed as %{fmpz_poly}.\n",
			state->epsilon);

		poly_sub_in_ring(state->epsilon_star, state->epsilon,
				 state->alpha, ctx->poly_ring_ctx);

		blind_sig_log_DEBUG_2_VALUES(
			ctx, "epsilon_star computed as %{fmpz_poly}.\n",
			state->epsilon_star);

		if (poly_all_abs_leq_ui(state->epsilon_star,
					ctx->d_epsilon_star)) {
			fmpz_poly_init(result->epsilon_star);
			fmpz_poly_set(result->epsilon_star,
				      state->epsilon_star);
			result->ok = true;

			break;
		}

		if (!(*retries_left && (*retries_left)--)) {
			result->ok = false;

			blind_sig_log_ERROR(
				ctx, "User failed to choose a proper alpha.\n");

			break;
		}

		blind_sig_log_INFO_1_CONTROL(
			ctx, "User retries with new alpha.\n");
	}

	fmpz_poly_clear(transformed_beta);
}

void blind_sig_proto_p3_init_do(blind_sig_proto_p3_t result,
				blind_sig_signer_state_t state,
				blind_sig_priv_key_t const priv_key,
				blind_sig_proto_p2_t const p2_result,
				blind_sig_ctx_t const ctx) {
	blind_sig_log_DEBUG_1_PHASES(ctx, "Protocol phase 3 started.\n");

	fmpz_poly_set(state->epsilon_star, p2_result->epsilon_star);

	for (ulong i = 0; i < ctx->m; i++) {
		poly_mul_in_ring(*state->z_star + i, *priv_key->key_polys + i,
				 state->epsilon_star, ctx->poly_ring_ctx);
		poly_add_in_ring(*state->z_star + i, *state->z_star + i,
				 *state->y_commitment_random + i,
				 ctx->poly_ring_ctx);

		if (!poly_all_abs_leq(*state->z_star + i, ctx->d_g_star)) {
			result->ok = false;

			blind_sig_log_INFO_1_CONTROL(
				ctx, "Restart triggered by signer from step"
				" 3.\n");

			return;
		}
	}

	blind_sig_poly_vector_init_set(result->z_star, state->z_star, ctx);

	result->ok = true;
}

void blind_sig_proto_p4_init_do(blind_sig_proto_p4_t result,
				blind_sig_t sig,
				blind_sig_user_state_t const state,
				blind_sig_proto_p3_t const p3_result,
				blind_sig_ctx_t const ctx) {
	blind_sig_poly_vector_t z;

	blind_sig_log_DEBUG_1_PHASES(ctx, "Protocol phase 4 started.\n");

	/* Rater that clearing z later, we shall use it in the result. */

	blind_sig_poly_vector_init(z, ctx);

	for (ulong i = 0; i < ctx->m; i++) {
		poly_sub_in_ring(*z + i,
				 *p3_result->z_star + i, *state->beta + i,
				 ctx->poly_ring_ctx);

		if (!poly_all_abs_leq(*z + i, ctx->d_g))
			goto fail;
	}

	blind_sig_message_init_set_buf(sig->message, state->message->buf,
				       state->message->bytes);

	blind_sig_n_bit_buf_init_set(sig->randomness, state->randomness, ctx);

	*sig->z = *z;

	fmpz_poly_init(sig->epsilon);
	fmpz_poly_set(sig->epsilon, state->epsilon);

	result->needs_restart = false;

	blind_sig_log_INFO_1_CONTROL(
		ctx, "User successfully obtained signature in step 4.\n");

	return;

fail:
	blind_sig_n_bit_buf_init_set(result->commitment, state->commitment,
				     ctx);

	fmpz_poly_init(result->alpha);
	fmpz_poly_set(result->alpha, state->alpha);

	/* Reuse z to store beta. */
	*result->beta = *z;

	for (ulong i = 0; i < ctx->m; i++)
		fmpz_poly_set(*result->beta + i, *state->beta + i);

	fmpz_poly_init(result->epsilon);
	fmpz_poly_set(result->epsilon, state->epsilon);

	result->needs_restart = true;

	blind_sig_log_INFO_1_CONTROL(
		ctx, "User couldn't get a signature in step 4, restart is being"
		" requested from signer.\n");
}

void blind_sig_proto_p5_init_do(blind_sig_proto_p5_t result,
				blind_sig_signer_state_t const state,
				blind_sig_pub_key_t const pub_key,
				blind_sig_proto_p4_t const p4_result,
				blind_sig_ctx_t const ctx) {
	fmpz_poly_t epsilon, tmp;
	blind_sig_poly_vector_t z = {NULL};

	blind_sig_log_DEBUG_1_PHASES(ctx, "Protocol phase 5 started.\n");

	fmpz_poly_init(epsilon);
	fmpz_poly_init(tmp);

	if (!p4_result->needs_restart) {
		blind_sig_log_INFO_1_CONTROL(
			ctx, "Signer acknowledged success in signing.\n");

		goto success;
	}

	/* Compute epsilon from alpha and epsilon_star, verify. */
	poly_add_in_ring(epsilon, state->epsilon_star, p4_result->alpha,
			 ctx->poly_ring_ctx);
	if (!fmpz_poly_equal(epsilon, p4_result->epsilon))
		goto failed_proving;

	/* Compute epsilon from alpha and beta, verify. */
	apply_homomorphism(tmp, p4_result->beta, ctx);

	poly_mul_in_ring(epsilon, pub_key->key_poly, p4_result->alpha,
			 ctx->poly_ring_ctx);
	fmpz_poly_neg(epsilon, epsilon);

	poly_add_in_ring(epsilon, epsilon, state->y_commitment,
			 ctx->poly_ring_ctx);

	poly_sub_in_ring(epsilon, epsilon, tmp, ctx->poly_ring_ctx);

	apply_hash(epsilon, epsilon, p4_result->commitment, ctx);

	if (!fmpz_poly_equal(epsilon, p4_result->epsilon))
		goto failed_proving;

	/* Compute epsilon from beta and z_star, verify. */
	blind_sig_poly_vector_init(z, ctx);

	for (ulong i = 0; i < ctx->m; i++) {
		poly_sub_in_ring(*z + i,
				 *state->z_star + i, *p4_result->beta + i,
				 ctx->poly_ring_ctx);
	}

	apply_homomorphism(tmp, z, ctx);

	poly_mul_in_ring(epsilon, epsilon, pub_key->key_poly,
			 ctx->poly_ring_ctx);
	fmpz_poly_neg(epsilon, epsilon);

	poly_add_in_ring(epsilon, epsilon, tmp, ctx->poly_ring_ctx);

	apply_hash(epsilon, epsilon, p4_result->commitment, ctx);

	if (!fmpz_poly_equal(epsilon, p4_result->epsilon))
		goto failed_proving;

	/* Verify that z really doesn't belong to its target domain. */
	for (ulong i = 0; i < ctx->m; i++) {
		if (!poly_all_abs_leq(*z + i, ctx->d_g)) {
			result->ok = false;

			blind_sig_log_INFO_1_CONTROL(
				ctx, "Restart triggered by signer from step"
				" 5.\n");

			goto out;
		}
	}

failed_proving:
	blind_sig_log_ERROR(
		ctx, "User failed to prove to the signer that obtaining"
		" signature failed :(\n");

success:
	blind_sig_poly_vector_init_set(result->y_commitment_random,
				       state->y_commitment_random, ctx);
	blind_sig_poly_vector_init_set(result->z_star, state->z_star, ctx);

	fmpz_poly_init(result->y_commitment);
	fmpz_poly_init(result->epsilon_star);

	fmpz_poly_set(result->y_commitment, state->y_commitment);
	fmpz_poly_set(result->epsilon_star, state->epsilon_star);

	result->ok = true;

out:
	fmpz_poly_clear(epsilon);
	fmpz_poly_clear(tmp);

	if (*z)
		blind_sig_poly_vector_t_clear(z, ctx);
}



/***
 *** Signature verification.
 ***/

bool blind_sig_verify(blind_sig_t const sig, blind_sig_pub_key_t const pub_key,
		      blind_sig_ctx_t const ctx) {
	blind_sig_n_bit_buf_t commitment;
	fmpz_poly_t epsilon;
	fmpz_poly_t transformed_z;
	bool result = true;

	blind_sig_n_bit_buf_init(commitment, ctx);
	fmpz_poly_init(epsilon);
	fmpz_poly_init(transformed_z);

	for (ulong i = 0; i < ctx->m; i++) {
		if (!poly_all_abs_leq(*sig->z + i, ctx->d_g))
			goto failed_verification;
	}

	ctx->public_commitment_function(*commitment, sig->message->buf,
					sig->message->bytes, *sig->randomness,
					ctx->n);

	poly_mul_in_ring(epsilon, sig->epsilon, pub_key->key_poly,
			 ctx->poly_ring_ctx);
	fmpz_poly_neg(epsilon, epsilon);

	apply_homomorphism(transformed_z, sig->z, ctx);

	poly_add_in_ring(epsilon, epsilon, transformed_z, ctx->poly_ring_ctx);

	apply_hash(epsilon, epsilon, commitment, ctx);

	if (!fmpz_poly_equal(epsilon, sig->epsilon))
		goto failed_verification;

	goto out;

failed_verification:
	result = false;

out:
	blind_sig_n_bit_buf_t_clear(commitment, ctx);
	fmpz_poly_clear(epsilon);
	fmpz_poly_clear(transformed_z);

	return result;
}