/* * SPDX-License-Identifier: CC0-1.0 * * Copyright (C) 2025 W. Kosior */ #define _POSIX_C_SOURCE 200809L /* for fmemopen() */ #include #include #include #include #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; }