From 1fb5d2a33716abc52d7d4e92f94590150f18d732 Mon Sep 17 00:00:00 2001 From: Eugene Kogan Date: Mon, 2 Dec 2024 10:08:43 +0000 Subject: [PATCH] upstream recent changes --- include/cosigner/cmp_ecdsa_signing_service.h | 4 +- .../cmp_ecdsa_offline_signing_service.cpp | 9 +- .../cmp_ecdsa_online_signing_service.cpp | 9 +- .../cosigner/cmp_ecdsa_signing_service.cpp | 18 +- src/common/cosigner/mta.cpp | 450 ++++++++- src/common/cosigner/mta.h | 145 ++- src/common/crypto/commitments/ring_pedersen.c | 2 +- src/common/crypto/drng/drng.c | 24 +- .../crypto/ed25519_algebra/ed25519_algebra.c | 2 +- src/common/crypto/paillier/paillier.c | 896 ++++++++++++++---- src/common/crypto/paillier/paillier_zkp.c | 806 +++++++++++++--- .../verifiable_secret_sharing.c | 17 +- .../zero_knowledge_proof/range_proofs.c | 6 + 13 files changed, 1974 insertions(+), 414 deletions(-) diff --git a/include/cosigner/cmp_ecdsa_signing_service.h b/include/cosigner/cmp_ecdsa_signing_service.h index a95466e..0653bf0 100644 --- a/include/cosigner/cmp_ecdsa_signing_service.h +++ b/include/cosigner/cmp_ecdsa_signing_service.h @@ -20,7 +20,7 @@ namespace cosigner namespace mta { - class response_verifier; + class base_response_verifier; } class cmp_key_persistency; @@ -101,7 +101,7 @@ class COSIGNER_EXPORT cmp_ecdsa_signing_service static cmp_mta_response create_mta_response(ecdsa_signing_data& data, const elliptic_curve256_algebra_ctx_t* algebra, uint64_t my_id, const std::vector& aad, const cmp_key_metadata& metadata, const std::map>& requests, size_t index, const elliptic_curve_scalar& key, const auxiliary_keys& aux_keys); static cmp_mta_deltas mta_verify(ecdsa_signing_data& data, const elliptic_curve256_algebra_ctx_t* algebra, uint64_t my_id, const std::string& uuid, const std::vector& aad, const cmp_key_metadata& metadata, - const std::map& mta_responses, size_t index, const auxiliary_keys& aux_keys, std::map& verifers); + const std::map& mta_responses, size_t index, const auxiliary_keys& aux_keys, std::map>& verifiers); static void calc_R(ecdsa_signing_data& data, elliptic_curve_point& R, const elliptic_curve256_algebra_ctx_t* algebra, uint64_t my_id, const std::string& uuid, const cmp_key_metadata& metadata, const std::map>& deltas, size_t index); diff --git a/src/common/cosigner/cmp_ecdsa_offline_signing_service.cpp b/src/common/cosigner/cmp_ecdsa_offline_signing_service.cpp index 206ef53..9a04894 100644 --- a/src/common/cosigner/cmp_ecdsa_offline_signing_service.cpp +++ b/src/common/cosigner/cmp_ecdsa_offline_signing_service.cpp @@ -200,7 +200,7 @@ uint64_t cmp_ecdsa_offline_signing_service::offline_mta_verify(const std::string } std::string uuid = metadata.key_id + request_id; - std::map verifers; + std::map> verifiers; for (auto it = mta_responses.begin(); it != mta_responses.end(); ++it) { if (it->first == my_id) @@ -208,8 +208,7 @@ uint64_t cmp_ecdsa_offline_signing_service::offline_mta_verify(const std::string const auto& other = key_md.players_info.at(it->first); auto aad = build_aad(uuid, it->first, key_md.seed); - mta::response_verifier verifer(it->first, algebra, aad, aux.paillier, other.paillier, aux.ring_pedersen); - verifers.emplace(it->first, std::move(verifer)); + verifiers[it->first] = mta::new_response_verifier(metadata.count, it->first, algebra, aad, aux.paillier, other.paillier, aux.ring_pedersen); } auto aad = build_aad(uuid, my_id, key_md.seed); @@ -217,7 +216,7 @@ uint64_t cmp_ecdsa_offline_signing_service::offline_mta_verify(const std::string { ecdsa_signing_data data; _preprocessing_persistency.load_preprocessing_data(request_id, metadata.start_index + i, data); - cmp_mta_deltas delta = mta_verify(data, algebra, my_id, uuid, aad, key_md, mta_responses, i, aux, verifers); + cmp_mta_deltas delta = mta_verify(data, algebra, my_id, uuid, aad, key_md, mta_responses, i, aux, verifiers); deltas.push_back(std::move(delta)); _preprocessing_persistency.store_preprocessing_data(request_id, metadata.start_index + i, data); } @@ -226,7 +225,7 @@ uint64_t cmp_ecdsa_offline_signing_service::offline_mta_verify(const std::string { if (it->first == my_id) continue; - verifers.at(it->first).verify(); + verifiers.at(it->first)->verify(); } return my_id; diff --git a/src/common/cosigner/cmp_ecdsa_online_signing_service.cpp b/src/common/cosigner/cmp_ecdsa_online_signing_service.cpp index 9e7a1e7..ec033c7 100644 --- a/src/common/cosigner/cmp_ecdsa_online_signing_service.cpp +++ b/src/common/cosigner/cmp_ecdsa_online_signing_service.cpp @@ -257,7 +257,7 @@ uint64_t cmp_ecdsa_online_signing_service::mta_verify(const std::string& txid, c } std::string uuid = metadata.key_id + txid; - std::map verifers; + std::map> verifiers; for (auto it = mta_responses.begin(); it != mta_responses.end(); ++it) { if (it->first == my_id) @@ -265,15 +265,14 @@ uint64_t cmp_ecdsa_online_signing_service::mta_verify(const std::string& txid, c const auto& other = key_md.players_info.at(it->first); auto aad = build_aad(uuid, it->first, key_md.seed); - mta::response_verifier verifer(it->first, algebra, aad, aux.paillier, other.paillier, aux.ring_pedersen); - verifers.emplace(it->first, std::move(verifer)); + verifiers[it->first] = mta::new_response_verifier(metadata.sig_data.size(), it->first, algebra, aad, aux.paillier, other.paillier, aux.ring_pedersen); } auto aad = build_aad(uuid, my_id, key_md.seed); for (size_t i = 0; i < metadata.sig_data.size(); i++) { cmp_signature_data& data = metadata.sig_data[i]; - cmp_mta_deltas delta = cmp_ecdsa_signing_service::mta_verify(data, algebra, my_id, uuid, aad, key_md, mta_responses, i, aux, verifers); + cmp_mta_deltas delta = cmp_ecdsa_signing_service::mta_verify(data, algebra, my_id, uuid, aad, key_md, mta_responses, i, aux, verifiers); deltas.push_back(std::move(delta)); } @@ -281,7 +280,7 @@ uint64_t cmp_ecdsa_online_signing_service::mta_verify(const std::string& txid, c { if (it->first == my_id) continue; - verifers.at(it->first).verify(); + verifiers.at(it->first)->verify(); } _signing_persistency.update_cmp_signing_data(txid, metadata); diff --git a/src/common/cosigner/cmp_ecdsa_signing_service.cpp b/src/common/cosigner/cmp_ecdsa_signing_service.cpp index 986ddc7..bc97f83 100644 --- a/src/common/cosigner/cmp_ecdsa_signing_service.cpp +++ b/src/common/cosigner/cmp_ecdsa_signing_service.cpp @@ -117,9 +117,19 @@ cmp_mta_response cmp_ecdsa_signing_service::create_mta_response(ecdsa_signing_da return resp; } -cmp_mta_deltas cmp_ecdsa_signing_service::mta_verify(ecdsa_signing_data& data, const elliptic_curve256_algebra_ctx_t* algebra, uint64_t my_id, const std::string& uuid, const std::vector& aad, const cmp_key_metadata& metadata, - const std::map& mta_responses, size_t index, const auxiliary_keys& aux_keys, std::map& verifers) +cmp_mta_deltas cmp_ecdsa_signing_service::mta_verify( + ecdsa_signing_data& data, //this block singing data + const elliptic_curve256_algebra_ctx_t* algebra, + uint64_t my_id, + const std::string& uuid, + const std::vector& aad, //this party's aad + const cmp_key_metadata& metadata, //all parties public metadata (public share, paillier, rind pedersen) + const std::map& mta_responses, //all responses from all parties + size_t index, //this block (message) index + const auxiliary_keys& aux_keys, + std::map >& verifiers) { + //iterate over all responses from all signers for (auto it = mta_responses.begin(); it != mta_responses.end(); ++it) { if (it->first == my_id) @@ -138,11 +148,11 @@ cmp_mta_deltas cmp_ecdsa_signing_service::mta_verify(ecdsa_signing_data& data, c } pub.gamma_commitment.clear(); cmp_mta_message& gamma_mta = const_cast(it->second.response[index].k_gamma_mta.at(my_id)); - verifers.at(it->first).process(data.mta_request, gamma_mta, pub.GAMMA); + verifiers.at(it->first)->process(data.mta_request, gamma_mta, pub.GAMMA); auto alpha = mta::decrypt_mta_response(it->first, algebra, std::move(gamma_mta.message), aux_keys.paillier); throw_cosigner_exception(algebra->add_scalars(algebra, &data.delta.data, data.delta.data, sizeof(elliptic_curve256_scalar_t), alpha.data, sizeof(elliptic_curve256_scalar_t))); cmp_mta_message& x_mta = const_cast(it->second.response[index].k_x_mta.at(my_id)); - verifers.at(it->first).process(data.mta_request, x_mta, other.public_share); + verifiers.at(it->first)->process(data.mta_request, x_mta, other.public_share); alpha = mta::decrypt_mta_response(it->first, algebra, std::move(x_mta.message), aux_keys.paillier); throw_cosigner_exception(algebra->add_scalars(algebra, &data.chi.data, data.chi.data, sizeof(elliptic_curve256_scalar_t), alpha.data, sizeof(elliptic_curve256_scalar_t))); throw_cosigner_exception(algebra->add_points(algebra, &data.GAMMA.data, &data.GAMMA.data, &pub.GAMMA.data)); diff --git a/src/common/cosigner/mta.cpp b/src/common/cosigner/mta.cpp index fda85a4..f20e396 100644 --- a/src/common/cosigner/mta.cpp +++ b/src/common/cosigner/mta.cpp @@ -423,12 +423,26 @@ static std::vector mta_range_generate_zkp(const elliptic_curve256_algeb return serialize_mta_range_zkp(proof, ring_pedersen, private_key, public_key); } -cmp_mta_message request(uint64_t my_id, const elliptic_curve256_algebra_ctx_t* algebra, const elliptic_curve_scalar& k, const elliptic_curve_scalar& gamma, const elliptic_curve_scalar& a, const elliptic_curve_scalar& b, - const byte_vector_t& aad, const std::shared_ptr& paillier, const std::map& players, std::map& proofs, std::map& G_proofs) +//implements phase 1 of ECDSA signing +//Since the cmp_mta_message has a common part for all parties and a specific part for each party +//this function prefills the common part and creates a map of proofs to feel the remaining part for each party individually. +cmp_mta_message request(const uint64_t my_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const elliptic_curve_scalar& k, //signing secret (randomness), saved on ecdsa_preprocessing_data state + const elliptic_curve_scalar& gamma, //signing secret (required for MtA), saved on ecdsa_preprocessing_data state + const elliptic_curve_scalar& a, //secret used for Rddh proof, saved on ecdsa_preprocessing_data state + const elliptic_curve_scalar& b, //secret used for Rddh proof, saved on ecdsa_preprocessing_data state + const byte_vector_t& aad, //additional authenticated data + const std::shared_ptr& paillier, //from key setup + const std::map& players, //maps all parties (players) ids to parameters from key setup phase + std::map& proofs, //output map all all parties (players) ids to Rddh proof messages + std::map& G_proofs) //output map all all parties (players) ids to "log" proof messages { cmp_mta_message mta; - paillier_ciphertext_t *ciphertext = NULL; + paillier_ciphertext_t *ciphertext = NULL; //will hold paillier encrypted k. Called K in the document long status = paillier_encrypt_to_ciphertext(paillier.get(), k.data, sizeof(elliptic_curve256_scalar_t), &ciphertext); + + //create self releasing guard in case of an exception thrown in the context std::unique_ptr ciphertext_guard(ciphertext, paillier_free_ciphertext); if (status != PAILLIER_SUCCESS) { @@ -596,15 +610,57 @@ elliptic_curve_scalar decrypt_mta_response(uint64_t other_id, const elliptic_cur return ret; } - -response_verifier::response_verifier(uint64_t other_id, const elliptic_curve256_algebra_ctx_t* algebra, const byte_vector_t& aad, const std::shared_ptr& my_key, - const std::shared_ptr& paillier, const std::shared_ptr& ring_pedersen) : - _other_id(other_id), _algebra(algebra), _aad(aad), _my_paillier(my_key), _my_ring_pedersen(ring_pedersen), _other_paillier(paillier), - _ctx(BN_CTX_new(), BN_CTX_free), _my_mont(BN_MONT_CTX_new(), BN_MONT_CTX_free), _other_mont(BN_MONT_CTX_new(), BN_MONT_CTX_free) +base_response_verifier::base_response_verifier(const uint64_t other_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const byte_vector_t& aad, + const std::shared_ptr& my_key, + const std::shared_ptr& paillier, + const std::shared_ptr& ring_pedersen) : + _other_id(other_id), + _algebra(algebra), + _aad(aad), + _my_paillier(my_key), + _my_ring_pedersen(ring_pedersen), + _other_paillier(paillier), + _ctx(BN_CTX_new(), BN_CTX_free), + _my_mont(BN_MONT_CTX_new(), BN_MONT_CTX_free), + _other_mont(BN_MONT_CTX_new(), BN_MONT_CTX_free) { if (!_ctx || !_my_mont || !_other_mont) throw cosigner_exception(cosigner_exception::NO_MEM); + BN_CTX_start(_ctx.get()); + + if (!BN_MONT_CTX_set(_my_mont.get(), _my_paillier->pub.n2, _ctx.get())) + { + LOG_ERROR("Failed to init montgomery context, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::NO_MEM); + } + if (!BN_MONT_CTX_set(_other_mont.get(), _other_paillier->n2, _ctx.get())) + { + LOG_ERROR("Failed to init montgomery context, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::NO_MEM); + } +} + +base_response_verifier::~base_response_verifier() +{ + if (_ctx) + { + BN_CTX_end(_ctx.get()); + } +} + +batch_response_verifier::batch_response_verifier( + uint64_t other_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const byte_vector_t& aad, + const std::shared_ptr& my_key, + const std::shared_ptr& paillier, + const std::shared_ptr& ring_pedersen) : + base_response_verifier(other_id, algebra, aad, my_key, paillier, ring_pedersen) +{ + for (size_t i = 0; i < BATCH_STATISTICAL_SECURITY; i++) { _mta_ro[i] = BN_CTX_get(_ctx.get()); @@ -614,7 +670,7 @@ response_verifier::response_verifier(uint64_t other_id, const elliptic_curve256_ if (!_mta_ro[i] || !_mta_B[i] || !_commitment_ro[i] || !_commitment_B[i]) { - LOG_ERROR("Failed to alloc bignums"); + LOG_ERROR("Failed to alloc batch bignums"); throw cosigner_exception(cosigner_exception::NO_MEM); } @@ -629,47 +685,17 @@ response_verifier::response_verifier(uint64_t other_id, const elliptic_curve256_ if (!_pedersen_t_exp || !_pedersen_B) { - LOG_ERROR("Failed toalloc bignums"); + LOG_ERROR("Failed to alloc pedersen bignums"); throw cosigner_exception(cosigner_exception::NO_MEM); } BN_one(_pedersen_B); - if (!BN_MONT_CTX_set(_my_mont.get(), _my_paillier->pub.n2, _ctx.get())) - { - LOG_ERROR("Failed to init montgomery context, error %lu", ERR_get_error()); - throw cosigner_exception(cosigner_exception::NO_MEM); - } - if (!BN_MONT_CTX_set(_other_mont.get(), _other_paillier->n2, _ctx.get())) - { - LOG_ERROR("Failed to init montgomery context, error %lu", ERR_get_error()); - throw cosigner_exception(cosigner_exception::NO_MEM); - } -} -response_verifier::response_verifier(response_verifier&& other) : - _other_id(other._other_id), _algebra(other._algebra), _aad(std::move(other._aad)), _my_paillier(other._my_paillier), _my_ring_pedersen(other._my_ring_pedersen), _other_paillier(other._other_paillier), - _ctx(std::move(other._ctx)), _my_mont(std::move(other._my_mont)), _other_mont(std::move(other._other_mont)) -{ - memcpy(_mta_B, other._mta_B, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memset(other._mta_B, 0, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memcpy(_mta_ro, other._mta_ro, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memset(other._mta_ro, 0, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memcpy(_commitment_B, other._commitment_B, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memset(other._commitment_B, 0, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memcpy(_commitment_ro, other._commitment_ro, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - memset(other._commitment_ro, 0, sizeof(BIGNUM*) * BATCH_STATISTICAL_SECURITY); - _pedersen_t_exp = other._pedersen_t_exp; - other._pedersen_t_exp = NULL; - _pedersen_B = other._pedersen_B; - other._pedersen_B = NULL; } -response_verifier::~response_verifier() -{ - if (_ctx) - BN_CTX_end(_ctx.get()); -} - -void response_verifier::process(const byte_vector_t& request, cmp_mta_message& response, const elliptic_curve_point& public_point) +void batch_response_verifier::process( + const byte_vector_t& request, //this is mta_request from ecdsa_preprocessing_data, K sent by the other party + cmp_mta_message& response, + const elliptic_curve_point& public_point) { bn_ctx_frame frame_guard(_ctx.get()); @@ -677,8 +703,12 @@ void response_verifier::process(const byte_vector_t& request, cmp_mta_message& r BIGNUM* mta_response = BN_CTX_get(_ctx.get()); BIGNUM* commitment = BN_CTX_get(_ctx.get()); if (!mta_request || !mta_response || !commitment || - !BN_bin2bn(request.data(), request.size(), mta_request) || !BN_bin2bn(response.message.data(), response.message.size(), mta_response) || !BN_bin2bn(response.commitment.data(), response.commitment.size(), commitment)) + !BN_bin2bn(request.data(), request.size(), mta_request) || + !BN_bin2bn(response.message.data(), response.message.size(), mta_response) || + !BN_bin2bn(response.commitment.data(), response.commitment.size(), commitment)) + { throw cosigner_exception(cosigner_exception::NO_MEM); + } BIGNUM* e = BN_CTX_get(_ctx.get()); @@ -759,7 +789,7 @@ void response_verifier::process(const byte_vector_t& request, cmp_mta_message& r process_ring_pedersen(e, proof); } -void response_verifier::verify() +void batch_response_verifier::verify() { bn_ctx_frame frame_guard(_ctx.get()); @@ -809,7 +839,7 @@ void response_verifier::verify() } } -void response_verifier::process_paillier(const BIGNUM* e, const BIGNUM* request, BIGNUM* response, const BIGNUM* commitment, const mta_range_zkp& proof) +void batch_response_verifier::process_paillier(const BIGNUM* e, const BIGNUM* request, BIGNUM* response, const BIGNUM* commitment, const mta_range_zkp& proof) { bn_ctx_frame frame_guard(_ctx.get()); @@ -828,21 +858,25 @@ void response_verifier::process_paillier(const BIGNUM* e, const BIGNUM* request, LOG_ERROR("response is not a valid ciphertext"); throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); } + if (is_coprime_fast(proof.A, _my_paillier->pub.n, _ctx.get()) != 1) { LOG_ERROR("proof A is not a valid ciphertext"); throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); } + if (is_coprime_fast(commitment, _other_paillier->n, _ctx.get()) != 1) { LOG_ERROR("commitment is not a valid ciphertext"); throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); } + if (is_coprime_fast(proof.By, _other_paillier->n, _ctx.get()) != 1) { LOG_ERROR("proof By is not a valid ciphertext"); throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); } + uint8_t random[2 * BATCH_STATISTICAL_SECURITY]; if (!RAND_bytes(random, 2 * BATCH_STATISTICAL_SECURITY * sizeof(uint8_t))) { @@ -951,7 +985,7 @@ void response_verifier::process_paillier(const BIGNUM* e, const BIGNUM* request, } } -void response_verifier::process_ring_pedersen(const BIGNUM* e, const mta_range_zkp& proof) +void batch_response_verifier::process_ring_pedersen(const BIGNUM* e, const mta_range_zkp& proof) { bn_ctx_frame frame_guard(_ctx.get()); @@ -1047,6 +1081,326 @@ void response_verifier::process_ring_pedersen(const BIGNUM* e, const mta_range_z } } +void single_response_verifier::process_ring_pedersen(const BIGNUM* e, const mta_range_zkp& proof) +{ + bn_ctx_frame frame_guard(_ctx.get()); + + BIGNUM* tmp1 = BN_CTX_get(_ctx.get()); + BIGNUM* tmp2 = BN_CTX_get(_ctx.get()); + + if (!tmp1 || !tmp2) + { + throw cosigner_exception(cosigner_exception::NO_MEM); + } + + auto rp_status = ring_pedersen_init_montgomery(&_my_ring_pedersen->pub, _ctx.get()); + if (rp_status != RING_PEDERSEN_SUCCESS) + { + LOG_ERROR("Failed to init ring pedersen motgomery context, error %d", rp_status); + throw cosigner_exception(cosigner_exception::NO_MEM); + } + + // s^z1*t^z3 == E*S^e + // tmp1 = z1* lamda + z3 + if (!BN_mod_mul(tmp1, _my_ring_pedersen->lamda, proof.z1, _my_ring_pedersen->phi_n, _ctx.get()) || + !BN_mod_add(tmp1, tmp1, proof.z3, _my_ring_pedersen->phi_n, _ctx.get())) + { + LOG_ERROR("Failed to calc lamda*z1+z3, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp1 = t^(lamda * z1 + z3) + if (!BN_mod_exp_mont(tmp1, _my_ring_pedersen->pub.t, tmp1, _my_ring_pedersen->pub.n, _ctx.get(), _my_ring_pedersen->pub.mont)) + { + LOG_ERROR("Failed to calc t^(lamda * z1 + z3), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = S^e + if (!BN_mod_exp_mont(tmp2, proof.S, e, _my_ring_pedersen->pub.n, _ctx.get(), _my_ring_pedersen->pub.mont)) + { + LOG_ERROR("Failed to calc S^e, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = tmp2 * E = E * S^e + if (!BN_mod_mul(tmp2, tmp2, proof.E, _my_ring_pedersen->pub.n, _ctx.get())) + { + LOG_ERROR("Failed to calc E * S^e), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //compare tmp1 and tmp2 + if (0 != BN_cmp(tmp1, tmp2)) + { + LOG_ERROR("s^z1*t^z3 != E * S^e)"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + // s^z2*t^z4 == F*T^e + // tmp1 = z2* lamda + z4 + if (!BN_mod_mul(tmp1, _my_ring_pedersen->lamda, proof.z2, _my_ring_pedersen->phi_n, _ctx.get()) || + !BN_mod_add(tmp1, tmp1, proof.z4, _my_ring_pedersen->phi_n, _ctx.get())) + { + LOG_ERROR("Failed to calc z2* lamda + z4, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp1 = t^(lamda * z2 + z4) + if (!BN_mod_exp_mont(tmp1, _my_ring_pedersen->pub.t, tmp1, _my_ring_pedersen->pub.n, _ctx.get(), _my_ring_pedersen->pub.mont)) + { + LOG_ERROR("Failed to calc t^(lamda * z2 + z4), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = T^e + if (!BN_mod_exp_mont(tmp2, proof.T, e, _my_ring_pedersen->pub.n, _ctx.get(), _my_ring_pedersen->pub.mont)) + { + LOG_ERROR("Failed to calc T^e, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = tmp2 * F = F * T^e + if (!BN_mod_mul(tmp2, tmp2, proof.F, _my_ring_pedersen->pub.n, _ctx.get())) + { + LOG_ERROR("Failed to calc F * T^e), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //compare tmp1 and tmp2 + if (0 != BN_cmp(tmp1, tmp2)) + { + LOG_ERROR("s^z2*t^z4 != F * T^e)"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } +} + +void single_response_verifier::process_paillier( + const BIGNUM* e, + const BIGNUM* request, //C in the document, actually here passed encrypted K + const BIGNUM* response, //D in the document, homomorphic calculation k*(x or gamma) + beta + const BIGNUM* commitment, //Y in the document, paillier encrypted my parties beta as commitment + const mta_range_zkp& proof) +{ + bn_ctx_frame frame_guard(_ctx.get()); + + BIGNUM* tmp1 = BN_CTX_get(_ctx.get()); + BIGNUM* tmp2 = BN_CTX_get(_ctx.get()); + + if (!tmp1 || !tmp2) + { + throw cosigner_exception(cosigner_exception::NO_MEM); + } + + if (is_coprime_fast(response, _my_paillier->pub.n, _ctx.get()) != 1) + { + LOG_ERROR("response is not a valid ciphertext"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + if (is_coprime_fast(proof.A, _my_paillier->pub.n, _ctx.get()) != 1) + { + LOG_ERROR("proof A is not a valid ciphertext"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + if (is_coprime_fast(commitment, _other_paillier->n, _ctx.get()) != 1) + { + LOG_ERROR("commitment is not a valid ciphertext"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + if (is_coprime_fast(proof.By, _other_paillier->n, _ctx.get()) != 1) + { + LOG_ERROR("proof By is not a valid ciphertext"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + //============ 1st MTA verification ============ + if (!BN_mod_exp_mont(tmp1, request, proof.z1, _my_paillier->pub.n2, _ctx.get(), _my_mont.get())) + { + LOG_ERROR("Failed to calc C^z1, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + long paillier_status = paillier_encrypt_openssl_internal(&_my_paillier->pub, tmp2, proof.w, proof.z2, _ctx.get()); + if (paillier_status != PAILLIER_SUCCESS) + { + LOG_ERROR("Failed to encrypt z2 with my key during verify, error %ld", paillier_status); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp1 holds the result + if (!BN_mod_mul(tmp1, tmp1, tmp2, _my_paillier->pub.n2, _ctx.get())) + { + LOG_ERROR("Failed to calc C^z1 * enc(z2, w), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = D^e + if (!BN_mod_exp_mont(tmp2, response, e, _my_paillier->pub.n2, _ctx.get(), _my_mont.get())) + { + LOG_ERROR("Failed to calc D^e, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = tmp2 * A = A * D^e + if (!BN_mod_mul(tmp2, tmp2, proof.A, _my_paillier->pub.n2, _ctx.get())) + { + LOG_ERROR("Failed to calc (A * D^e), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //compare tmp1 and tmp2 + if (0 != BN_cmp(tmp1, tmp2)) + { + LOG_ERROR("Failed check C^z1 * enc(z2, w) == A * D^e"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + //============ 2nd MTA verification ============ + paillier_status = paillier_encrypt_openssl_internal(_other_paillier.get(), tmp1, proof.wy, proof.z2, _ctx.get()); + if (paillier_status != PAILLIER_SUCCESS) + { + LOG_ERROR("Failed to encrypt z2 with my peer's key during verify, error %ld", paillier_status); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = Y^e + if (!BN_mod_exp_mont(tmp2, commitment, e, _other_paillier->n2, _ctx.get(), _other_mont.get())) + { + LOG_ERROR("Failed to calc Y^e, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //tmp2 = tmp2 * By = By * Y^e + if (!BN_mod_mul(tmp2, proof.By, tmp2, _other_paillier->n2, _ctx.get())) + { + LOG_ERROR("Failed to calc (A * D^e), error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + //compare tmp1 and tmp2 + if (0 != BN_cmp(tmp1, tmp2)) + { + LOG_ERROR("Failed check enc(z2, w) == By * Y^e"); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } +} + +void single_response_verifier::process(const byte_vector_t& request, cmp_mta_message& response, const elliptic_curve_point& public_point) +{ + bn_ctx_frame ctx_guard(_ctx.get()); + + BIGNUM* mta_request = BN_CTX_get(_ctx.get()); + + if (!mta_request || !BN_bin2bn(request.data(), request.size(), mta_request)) + { + throw cosigner_exception(cosigner_exception::NO_MEM); + } + + + BIGNUM* mta_response = BN_CTX_get(_ctx.get()); //paillier encrypted minus my beta with my key + BIGNUM* commitment = BN_CTX_get(_ctx.get()); //paillier encrypted my parties beta with his key + if (!mta_response || !commitment || + !BN_bin2bn(response.message.data(), response.message.size(), mta_response) || + !BN_bin2bn(response.commitment.data(), response.commitment.size(), commitment)) + { + throw cosigner_exception(cosigner_exception::NO_MEM); + } + + BIGNUM* e = BN_CTX_get(_ctx.get()); + + if (!e) + throw cosigner_exception(cosigner_exception::NO_MEM); + + mta_range_zkp proof(_ctx.get()); + + deserialize_mta_range_zkp(response.proof, &_my_ring_pedersen->pub, _my_paillier.get(), _other_paillier.get(), proof); + + // start with range check + if ((size_t)BN_num_bytes(proof.z1) > sizeof(elliptic_curve256_scalar_t) + MTA_ZKP_EPSILON_SIZE) + { + LOG_ERROR("player %lu z1 (%d bits) is out of range", _other_id, BN_num_bits(proof.z1)); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + if ((size_t)BN_num_bytes(proof.z2) > sizeof(elliptic_curve256_scalar_t) * BETA_HIDING_FACTOR + MTA_ZKP_EPSILON_SIZE) + { + LOG_ERROR("player %lu z2 (%d bits) is out of range", _other_id, BN_num_bits(proof.z2)); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + // sample e + uint8_t seed[SHA256_DIGEST_LENGTH]; + genarate_mta_range_zkp_seed(response, proof, _aad, seed); + + response.commitment.clear(); + response.proof.clear(); + + drng_t* rng = NULL; + if (drng_new(seed, SHA256_DIGEST_LENGTH, &rng) != DRNG_SUCCESS) + { + LOG_ERROR("Failed to create drng"); + throw cosigner_exception(cosigner_exception::NO_MEM); + } + std::unique_ptr drng_guard(rng, drng_free); + + const BIGNUM* q = _algebra->order_internal(_algebra); + elliptic_curve256_scalar_t val; + do + { + drng_read_deterministic_rand(rng, val, sizeof(elliptic_curve256_scalar_t)); + if (!BN_bin2bn(val, sizeof(elliptic_curve256_scalar_t), e)) + { + LOG_ERROR("Failed to load e, error %lu", ERR_get_error()); + throw cosigner_exception(cosigner_exception::NO_MEM); + } + } while (BN_cmp(e, q) >= 0); + drng_guard.reset(); + + elliptic_curve256_point_t p1, p2; + + //scope for bin variable and calculate p1=q^z1 + { + std::vector bin(BN_num_bytes(proof.z1)); + BN_bn2bin(proof.z1, bin.data()); + auto status = _algebra->generator_mul_data(_algebra, bin.data(), bin.size(), &p1); + if (status != ELLIPTIC_CURVE_ALGEBRA_SUCCESS) + { + LOG_ERROR("Failed to calc g^z1, error %d", status); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + } + + auto status = _algebra->point_mul(_algebra, &p2, &public_point.data, &val); + if (status != ELLIPTIC_CURVE_ALGEBRA_SUCCESS) + { + LOG_ERROR("Failed to calc X^e, error %d", status); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + status = _algebra->add_points(_algebra, &p2, &proof.Bx, &p2); + if (status != ELLIPTIC_CURVE_ALGEBRA_SUCCESS) + { + LOG_ERROR("Failed to calc Bx*X^e, error %d", status); + throw cosigner_exception(cosigner_exception::INTERNAL_ERROR); + } + + // verify g^z1 == Bx*X^e + if (memcmp(p1, p2, sizeof(elliptic_curve256_point_t)) != 0) + { + LOG_ERROR("Failed to verify Bx*X^e == g^z1 for player %lu", _other_id); + throw cosigner_exception(cosigner_exception::INVALID_PARAMETERS); + } + + process_paillier(e, + mta_request, + mta_response, + commitment, + proof); + process_ring_pedersen(e, proof); +} + } } } diff --git a/src/common/cosigner/mta.h b/src/common/cosigner/mta.h index dba2f70..47a58c4 100644 --- a/src/common/cosigner/mta.h +++ b/src/common/cosigner/mta.h @@ -24,28 +24,49 @@ struct cmp_player_info; namespace mta { -cmp_mta_message request(uint64_t my_id, const elliptic_curve256_algebra_ctx_t* algebra, const elliptic_curve_scalar& k, const elliptic_curve_scalar& gamma, const elliptic_curve_scalar& a, const elliptic_curve_scalar& b, - const byte_vector_t& aad, const std::shared_ptr& paillier, const std::map& players, std::map& proofs, std::map& G_proofs); -elliptic_curve_scalar answer_mta_request(const elliptic_curve256_algebra_ctx_t* algebra, const cmp_mta_message& request, const uint8_t* secret, uint32_t secret_size, const byte_vector_t& aad, - const std::shared_ptr& my_key, const std::shared_ptr& paillier, const std::shared_ptr& ring_pedersen, cmp_mta_message& response); -elliptic_curve_scalar decrypt_mta_response(uint64_t other_id, const elliptic_curve256_algebra_ctx_t* algebra, byte_vector_t&& response, const std::shared_ptr& my_key); +cmp_mta_message request(const uint64_t my_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const elliptic_curve_scalar& k, + const elliptic_curve_scalar& gamma, + const elliptic_curve_scalar& a, + const elliptic_curve_scalar& b, + const byte_vector_t& aad, + const std::shared_ptr& paillier, + const std::map& players, + std::map& proofs, + std::map& G_proofs); + +elliptic_curve_scalar answer_mta_request(const elliptic_curve256_algebra_ctx_t* algebra, + const cmp_mta_message& request, + const uint8_t* secret, + uint32_t secret_size, + const byte_vector_t& aad, + const std::shared_ptr& my_key, + const std::shared_ptr& paillier, + const std::shared_ptr& ring_pedersen, + cmp_mta_message& response); + +elliptic_curve_scalar decrypt_mta_response(uint64_t other_id, + const elliptic_curve256_algebra_ctx_t* algebra, + byte_vector_t&& response, + const std::shared_ptr& my_key); struct mta_range_zkp; -class response_verifier +class base_response_verifier { public: - response_verifier(uint64_t other_id, const elliptic_curve256_algebra_ctx_t* algebra, const byte_vector_t& aad, const std::shared_ptr& my_key, - const std::shared_ptr& paillier, const std::shared_ptr& ring_pedersen); - response_verifier(response_verifier&& other); - ~response_verifier(); + base_response_verifier(const uint64_t other_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const byte_vector_t& aad, + const std::shared_ptr& my_key, + const std::shared_ptr& paillier, + const std::shared_ptr& ring_pedersen); + + virtual ~base_response_verifier(); - void process(const byte_vector_t& request, cmp_mta_message& response, const elliptic_curve_point& public_point); - void verify(); -private: - void process_paillier(const bignum_st* e, const bignum_st* request, bignum_st* response, const bignum_st* commitment, const mta_range_zkp& proof); - void process_ring_pedersen(const bignum_st* e, const mta_range_zkp& proof); +protected: const uint64_t _other_id; const elliptic_curve256_algebra_ctx_t* _algebra; const byte_vector_t _aad; @@ -53,18 +74,104 @@ class response_verifier const std::shared_ptr _my_ring_pedersen; const std::shared_ptr _other_paillier; - static const size_t BATCH_STATISTICAL_SECURITY = 5; + std::unique_ptr _ctx; + std::unique_ptr _my_mont; + std::unique_ptr _other_mont; + +public: + //process single request. + virtual void process(const byte_vector_t& request, cmp_mta_message& response, const elliptic_curve_point& public_point) = 0; + //must be called to finalize multiple request processing. + virtual void verify() = 0; + +}; + +//batch verifier, optimized for verifying at least MIN_BATCH_SIZE operations +class batch_response_verifier : public base_response_verifier +{ +public: + batch_response_verifier(const uint64_t other_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const byte_vector_t& aad, + const std::shared_ptr& my_key, + const std::shared_ptr& paillier, + const std::shared_ptr& ring_pedersen); + + virtual ~batch_response_verifier() = default; + + virtual void process(const byte_vector_t& request, cmp_mta_message& response, const elliptic_curve_point& public_point) override; + virtual void verify() override; + + + +private: + void process_paillier(const bignum_st* e, + const bignum_st* request, + bignum_st* response, + const bignum_st* commitment, + const mta_range_zkp& proof); + + void process_ring_pedersen(const bignum_st* e, + const mta_range_zkp& proof); + + + + static constexpr const size_t BATCH_STATISTICAL_SECURITY = 5; bignum_st* _mta_ro[BATCH_STATISTICAL_SECURITY]; bignum_st* _mta_B[BATCH_STATISTICAL_SECURITY]; bignum_st* _commitment_ro[BATCH_STATISTICAL_SECURITY]; bignum_st* _commitment_B[BATCH_STATISTICAL_SECURITY]; bignum_st* _pedersen_t_exp; bignum_st* _pedersen_B; - std::unique_ptr _ctx; - std::unique_ptr _my_mont; - std::unique_ptr _other_mont; + +public: + + //minimal number of MTA calculations that worth to be batched. + //Note: this is not number of blocks. In EcDSA signature there are 2 MTA in each block + static constexpr const size_t MIN_BATCH_SIZE = BATCH_STATISTICAL_SECURITY + 1; +}; + +//regular verifier - work on a single MTA +class single_response_verifier : public base_response_verifier +{ +public: + using base_response_verifier::base_response_verifier; + + virtual ~single_response_verifier() = default; + + virtual void process(const byte_vector_t& request, cmp_mta_message& response, const elliptic_curve_point& public_point) override; + + virtual void verify() override {} //empty since each request processed individually. +private: + void process_paillier(const bignum_st* e, + const bignum_st* request, //C in the document, actually here passed encrypted K + const bignum_st* response, //D in the document, homomorphic calculation k*(x or gamma) + beta + const bignum_st* commitment, //Y in the document, paillier encrypted my parties beta as commitment + const mta_range_zkp& proof); + + void process_ring_pedersen(const bignum_st* e, const mta_range_zkp& proof); }; +static inline std::unique_ptr new_response_verifier( + const size_t num_of_blocks, + const uint64_t other_id, + const elliptic_curve256_algebra_ctx_t* algebra, + const byte_vector_t& aad, + const std::shared_ptr& my_key, + const std::shared_ptr& paillier, + const std::shared_ptr& ring_pedersen, + const size_t min_batch_threshold = batch_response_verifier::MIN_BATCH_SIZE) +{ + if (num_of_blocks >= min_batch_threshold) + { + return std::unique_ptr(new batch_response_verifier(other_id, algebra, aad, my_key, paillier, ring_pedersen)); + } + else + { + return std::unique_ptr(new single_response_verifier(other_id, algebra, aad, my_key, paillier, ring_pedersen)); + } +} + } } diff --git a/src/common/crypto/commitments/ring_pedersen.c b/src/common/crypto/commitments/ring_pedersen.c index 8338e52..9f86ae3 100644 --- a/src/common/crypto/commitments/ring_pedersen.c +++ b/src/common/crypto/commitments/ring_pedersen.c @@ -885,11 +885,11 @@ ring_pedersen_status ring_pedersen_verify_batch_commitments(const ring_pedersen_ B = BN_CTX_get(ctx); tmp1 = BN_CTX_get(ctx); tmp2 = BN_CTX_get(ctx); - BN_one(B); if (!t_exp || !B || !tmp1 || !tmp2) goto cleanup; + BN_one(B); status = RING_PEDERSEN_UNKNOWN_ERROR; ring_pedersen_init_mont(&priv->pub, ctx); diff --git a/src/common/crypto/drng/drng.c b/src/common/crypto/drng/drng.c index 8e2f932..cd6177a 100644 --- a/src/common/crypto/drng/drng.c +++ b/src/common/crypto/drng/drng.c @@ -16,12 +16,17 @@ drng_status drng_new(const uint8_t *seed, uint32_t seed_len, drng_t **rng) drng_t *local_rng = NULL; if (!seed || !seed_len || !rng) + { return DRNG_INVALID_PARAMETER; - + } + local_rng = malloc(sizeof(drng_t)); if (!local_rng) + { return DRNG_OUT_OF_MEMORY; + } + local_rng->pos = 0; SHA512(seed, seed_len, local_rng->data); // data and seed are continuous in memory so SHA512 function will initialize both the data and the seed for the next operation @@ -41,18 +46,31 @@ void drng_free(drng_t *rng) drng_status drng_read_deterministic_rand(drng_t *rng, uint8_t *rand, uint32_t length_in_bytes) { if (!rng || !rand || !length_in_bytes) + { return DRNG_INVALID_PARAMETER; - + } + + + // prevent buffer overflow / memory corruption in case rand buffer points to the drng data structure + if ((rand >= rng->data && rand <= (uint8_t*)(rng + 1)) || + (rand < rng->data && rand + length_in_bytes > (uint8_t*)rng)) + { + return DRNG_INVALID_PARAMETER; + } + + // since rng->pos is always <= sizeof(rng->data) while (length_in_bytes > sizeof(rng->data) - rng->pos) { - uint8_t size = sizeof(rng->data) - rng->pos; + const uint8_t size = sizeof(rng->data) - rng->pos; memcpy(rand, rng->data + rng->pos, size); rand += size; length_in_bytes -= size; rng->pos = 0; SHA512(rng->seed, sizeof(rng->seed), rng->data); } + memcpy(rand, rng->data + rng->pos, length_in_bytes); rng->pos += length_in_bytes; + return DRNG_SUCCESS; } diff --git a/src/common/crypto/ed25519_algebra/ed25519_algebra.c b/src/common/crypto/ed25519_algebra/ed25519_algebra.c index 4d59cfa..1b33d72 100644 --- a/src/common/crypto/ed25519_algebra/ed25519_algebra.c +++ b/src/common/crypto/ed25519_algebra/ed25519_algebra.c @@ -772,7 +772,7 @@ static elliptic_curve_algebra_status generator_mul(const elliptic_curve256_algeb return ELLIPTIC_CURVE_ALGEBRA_INVALID_PARAMETER; (*res)[sizeof(ed25519_point_t)] = 0; - return ed25519_algebra_generator_mul_data(ctx->ctx, *exp, sizeof(elliptic_curve256_scalar_t), (ed25519_point_t*)res);; + return ed25519_algebra_generator_mul_data(ctx->ctx, *exp, sizeof(elliptic_curve256_scalar_t), (ed25519_point_t*)res); } static elliptic_curve_algebra_status add_points(const elliptic_curve256_algebra_ctx_t *ctx, elliptic_curve256_point_t *res, const elliptic_curve256_point_t *p1, const elliptic_curve256_point_t *p2) diff --git a/src/common/crypto/paillier/paillier.c b/src/common/crypto/paillier/paillier.c index 23aea29..9ba8b1a 100644 --- a/src/common/crypto/paillier/paillier.c +++ b/src/common/crypto/paillier/paillier.c @@ -8,17 +8,22 @@ // WARNING: this function doesn't run in constant time! int is_coprime_fast(const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) { - BIGNUM *a, *b; - int ret = -1; - BN_CTX_start(ctx); - a = BN_CTX_get(ctx); - b = BN_CTX_get(ctx); + BIGNUM *a, *b; + int ret = -1; + + BN_CTX_start(ctx); + a = BN_CTX_get(ctx); + b = BN_CTX_get(ctx); - if (!a || !BN_copy(a, in_a)) - goto cleanup; + if (!a || !BN_copy(a, in_a)) + { + goto cleanup; + } if (!b || !BN_copy(b, in_b)) - goto cleanup; + { + goto cleanup; + } if (BN_cmp(a, b) < 0) { @@ -27,19 +32,22 @@ int is_coprime_fast(const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) a = t; } - while (!BN_is_zero(b)) - { + while (!BN_is_zero(b)) + { BIGNUM *t; - if (!BN_mod(a, a, b, ctx)) - goto cleanup; + if (!BN_mod(a, a, b, ctx)) + { + goto cleanup; + } t = b; - b = a; - a = t; - } - ret = BN_is_one(a); + b = a; + a = t; + } + ret = BN_is_one(a); + cleanup: - BN_CTX_end(ctx); - return ret; + BN_CTX_end(ctx); + return ret; } static uint64_t L(BIGNUM *res, const BIGNUM *x, const BIGNUM *n, BN_CTX *ctx) @@ -47,15 +55,23 @@ static uint64_t L(BIGNUM *res, const BIGNUM *x, const BIGNUM *n, BN_CTX *ctx) uint64_t ret = -1; BIGNUM *x_copy = BN_dup(x); + if (!BN_sub_word(x_copy, 1)) + { goto cleanup; + } + if (!BN_div(res, NULL, x_copy, n, ctx)) + { goto cleanup; + } - ret = 0; + ret = PAILLIER_SUCCESS; cleanup: if (ret) - ret = ERR_get_error(); + { + ret = ERR_get_error() * -1; + } BN_clear_free(x_copy); return ret; @@ -64,26 +80,39 @@ static uint64_t L(BIGNUM *res, const BIGNUM *x, const BIGNUM *n, BN_CTX *ctx) long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, paillier_private_key_t **priv) { long ret = -1; - BIGNUM *p, *q, *tmp, *n, *n2, *lamda, *mu, *three, *four; + BIGNUM *p = NULL, *q = NULL; + BIGNUM *tmp = NULL, *n = NULL, *n2 = NULL; + BIGNUM *lamda = NULL, *mu = NULL; + BIGNUM *three = NULL, *seven = NULL, *eight = NULL; BN_CTX *ctx = NULL; paillier_public_key_t *local_pub = NULL; paillier_private_key_t *local_priv = NULL; if (!pub || !priv) + { return PAILLIER_ERROR_INVALID_PARAM; + } if (key_len < MIN_KEY_LEN_IN_BITS) + { return PAILLIER_ERROR_KEYLEN_TOO_SHORT; - if ((ctx = BN_CTX_secure_new()) == NULL) - return ERR_get_error() * -1; + } *pub = NULL; *priv = NULL; + ctx = BN_CTX_secure_new(); + if (!ctx) + { + //not jumping to cleanup to avoid initializating all local variables + return PAILLIER_ERROR_OUT_OF_MEMORY; + } + BN_CTX_start(ctx); tmp = BN_CTX_get(ctx); three = BN_CTX_get(ctx); - four = BN_CTX_get(ctx); + seven = BN_CTX_get(ctx); + eight = BN_CTX_get(ctx); p = BN_new(); q = BN_new(); @@ -92,8 +121,10 @@ long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, p lamda = BN_new(); mu = BN_new(); - if (!p || !q || !tmp || !n || !n2 || !mu || !lamda || !three || !four) + if (!p || !q || !tmp || !n || !n2 || !lamda || !mu || !three || !seven || !eight) + { goto cleanup; + } BN_set_flags(n, BN_FLG_CONSTTIME); BN_set_flags(n2, BN_FLG_CONSTTIME); @@ -103,41 +134,77 @@ long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, p BN_set_flags(mu, BN_FLG_CONSTTIME); if (!BN_set_word(three, 3)) + { + goto cleanup; + } + + if (!BN_set_word(seven, 7)) + { goto cleanup; - if (!BN_set_word(four, 4)) + } + + if (!BN_set_word(eight, 8)) + { goto cleanup; + } // Choose two large prime p,q numbers having gcd(pq, (p-1)(q-1)) == 1 do + { // note - originally we had used p and q to be 4*k + 3. The new form keeps this requirement because + // both p and q still satisfies 4 * k + 3 + + // p needs to be in the form of p = 8 * k + 3 ( p = 3 mod 8) to allow efficient calculation off fourth roots + // (needed in paillier blum zkp) + if (!BN_generate_prime_ex(p, key_len / 2, 0, eight, three, NULL)) { - // p and q needs to be in the form of p = 4 * k + 3 to allow efficient calculation off fourth roots (needed in paillier blum zkp) - if (!BN_generate_prime_ex(p, key_len / 2, 0, four, three, NULL)) goto cleanup; - if (!BN_generate_prime_ex(q, key_len / 2, 0, four, three, NULL)) + } + + // and set must be q = 7 mod 8 (8 * k + 7) + if (!BN_generate_prime_ex(q, key_len / 2, 0, eight, seven, NULL)) + { goto cleanup; + } if (BN_num_bits(p) != BN_num_bits(q)) + { continue; + } // Compute n = pq if (!BN_mul(n, p, q, ctx)) + { goto cleanup; + } if (!BN_sub(lamda, n, p)) + { goto cleanup; + } + if (!BN_sub(lamda, lamda, q)) + { goto cleanup; + } + if (!BN_add_word(lamda, 1)) + { goto cleanup; } - while (BN_cmp(p, q) == 0 || !BN_gcd(tmp, lamda, n, ctx) || !BN_is_one(tmp)); + } while (BN_cmp(p, q) == 0 || + !BN_gcd(tmp, lamda, n, ctx) || + !BN_is_one(tmp)); if (!BN_sqr(n2, n, ctx)) + { goto cleanup; + } // if num_bits(q) == num_bits(p), we can optimize g lambda and mu selection see https://en.wikipedia.org/wiki/Paillier_cryptosystem if (!BN_mod_inverse(mu, lamda, n, ctx)) + { goto cleanup; + } local_priv = (paillier_private_key_t*)malloc(sizeof(paillier_private_key_t)); if (!local_priv) @@ -158,6 +225,7 @@ long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, p ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; } + local_pub->n = BN_dup(n); local_pub->n2 = BN_dup(n2); @@ -170,10 +238,12 @@ long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, p *priv = local_priv; *pub = local_pub; - ret = 0; + ret = PAILLIER_SUCCESS; cleanup: - if (ret == -1) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } if (ctx) { @@ -185,7 +255,9 @@ long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, p { // handle errors if (local_priv) + { free(local_priv); + } paillier_free_public_key(local_pub); // as the public key uses duplication of n and n2 it's not sefficent just to free it BN_free(p); BN_free(q); @@ -200,23 +272,38 @@ long paillier_generate_key_pair(uint32_t key_len, paillier_public_key_t **pub, p long paillier_public_key_n(const paillier_public_key_t *pub, uint8_t *n, uint32_t n_len, uint32_t *n_real_len) { - uint32_t len; + uint32_t len = 0; if (!pub) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!n && n_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + len = BN_num_bytes(pub->n); + if (n_real_len) + { *n_real_len = len; + } + if (n_len < len) + { return PAILLIER_ERROR_KEYLEN_TOO_SHORT; + } + return BN_bn2bin(pub->n, n) > 0 ? PAILLIER_SUCCESS : ERR_get_error() * -1; } uint32_t paillier_public_key_size(const paillier_public_key_t *pub) { if (pub) + { return BN_num_bytes(pub->n) * 8; + } + return 0; } @@ -227,34 +314,54 @@ uint8_t *paillier_public_key_serialize(const paillier_public_key_t *pub, uint8_t uint8_t *p = buffer; if (!pub) + { return NULL; + } + n_len = (uint32_t)BN_num_bytes(pub->n); needed_len = sizeof(uint32_t) + n_len; + if (real_buffer_len) + { *real_buffer_len = needed_len; + } + if (!buffer || buffer_len < needed_len) + { return NULL; + } + memcpy(p, &n_len, sizeof(uint32_t)); p += sizeof(uint32_t); BN_bn2bin(pub->n, p); return buffer; } + paillier_public_key_t *paillier_public_key_deserialize(const uint8_t *buffer, uint32_t buffer_len) { - paillier_public_key_t *pub; + paillier_public_key_t *pub = NULL; uint32_t len = 0; BN_CTX *ctx = NULL; if (!buffer || buffer_len < (sizeof(uint32_t) + MIN_KEY_LEN_IN_BITS / 8)) + { return NULL; + } + pub = (paillier_public_key_t*)calloc(1, sizeof(paillier_public_key_t)); if (!pub) + { return NULL; + } memcpy(&len, buffer, sizeof(uint32_t)); assert(len == (buffer_len - sizeof(uint32_t))); + if (len > (buffer_len - sizeof(uint32_t))) + { goto cleanup; + } + buffer_len -= sizeof(uint32_t); buffer += sizeof(uint32_t); @@ -262,14 +369,21 @@ paillier_public_key_t *paillier_public_key_deserialize(const uint8_t *buffer, ui pub->n2 = BN_new(); if (!pub->n || !pub->n2) + { goto cleanup; + } if (BN_num_bits(pub->n) < MIN_KEY_LEN_IN_BITS) + { goto cleanup; + } ctx = BN_CTX_new(); if (!ctx || !BN_sqr(pub->n2, pub->n, ctx)) + { goto cleanup; + } + BN_CTX_free(ctx); return pub; @@ -291,23 +405,37 @@ void paillier_free_public_key(paillier_public_key_t *pub) long paillier_private_key_n(const paillier_private_key_t *priv, uint8_t *n, uint32_t n_len, uint32_t *n_real_len) { - uint32_t len; + uint32_t len = 0; if (!priv) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!n && n_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + len = BN_num_bytes(priv->pub.n); if (n_real_len) + { *n_real_len = len; + } + if (n_len < len) + { return PAILLIER_ERROR_KEYLEN_TOO_SHORT; + } + return BN_bn2bin(priv->pub.n, n) > 0 ? PAILLIER_SUCCESS : ERR_get_error() * -1; } const paillier_public_key_t* paillier_private_key_get_public(const paillier_private_key_t *priv) { if (priv) + { return &priv->pub; + } return NULL; } @@ -318,14 +446,24 @@ uint8_t *paillier_private_key_serialize(const paillier_private_key_t *priv, uint uint8_t *p = buffer; if (!priv) + { return NULL; + } + p_len = (uint32_t)BN_num_bytes(priv->p); assert(p_len == (uint32_t)BN_num_bytes(priv->q)); needed_len = sizeof(uint32_t) + 2 * p_len; + if (real_buffer_len) + { *real_buffer_len = needed_len; + } + if (!buffer || buffer_len < needed_len) + { return NULL; + } + memcpy(p, &p_len, sizeof(uint32_t)); p += sizeof(uint32_t); BN_bn2bin(priv->p, p); @@ -336,20 +474,30 @@ uint8_t *paillier_private_key_serialize(const paillier_private_key_t *priv, uint paillier_private_key_t *paillier_private_key_deserialize(const uint8_t *buffer, uint32_t buffer_len) { - paillier_private_key_t *priv; + paillier_private_key_t *priv = NULL; uint32_t len = 0; BN_CTX *ctx = NULL; if (!buffer || buffer_len < (sizeof(uint32_t) + MIN_KEY_LEN_IN_BITS / 8)) // len(p) + len(q) == len(n) + { return NULL; + } + priv = (paillier_private_key_t*)calloc(1, sizeof(paillier_private_key_t)); + if (!priv) + { return NULL; + } memcpy(&len, buffer, sizeof(uint32_t)); + assert(2 * len == (buffer_len - sizeof(uint32_t))); if (2 * len > (buffer_len - sizeof(uint32_t))) + { goto cleanup; + } + buffer_len -= sizeof(uint32_t); buffer += sizeof(uint32_t); @@ -362,7 +510,9 @@ paillier_private_key_t *paillier_private_key_deserialize(const uint8_t *buffer, priv->mu = BN_new(); if (!priv->p || !priv->q || !priv->lamda || !priv->mu || !priv->pub.n || !priv->pub.n2) + { goto cleanup; + } BN_set_flags(priv->p, BN_FLG_CONSTTIME); BN_set_flags(priv->q, BN_FLG_CONSTTIME); @@ -370,30 +520,53 @@ paillier_private_key_t *paillier_private_key_deserialize(const uint8_t *buffer, BN_set_flags(priv->pub.n2, BN_FLG_CONSTTIME); BN_set_flags(priv->lamda, BN_FLG_CONSTTIME); BN_set_flags(priv->mu, BN_FLG_CONSTTIME); - ctx = BN_CTX_new(); + ctx = BN_CTX_new(); if (!ctx) + { goto cleanup; + } + BN_CTX_start(ctx); if (!BN_mul(priv->pub.n, priv->p, priv->q, ctx)) + { goto cleanup; + } + if (!BN_sqr(priv->pub.n2, priv->pub.n, ctx)) + { goto cleanup; + } + if (!BN_sub(priv->lamda, priv->pub.n, priv->p)) + { goto cleanup; + } + if (!BN_sub(priv->lamda, priv->lamda, priv->q)) + { goto cleanup; + } + if (!BN_add_word(priv->lamda, 1)) + { goto cleanup; + } + if (!BN_mod_inverse(priv->mu, priv->lamda, priv->pub.n, ctx)) + { goto cleanup; + } if (BN_num_bits(priv->pub.n) < MIN_KEY_LEN_IN_BITS) + { goto cleanup; + } BN_CTX_end(ctx); BN_CTX_free(ctx); + return priv; cleanup: @@ -402,6 +575,7 @@ paillier_private_key_t *paillier_private_key_deserialize(const uint8_t *buffer, BN_CTX_end(ctx); BN_CTX_free(ctx); } + paillier_free_private_key(priv); return NULL; } @@ -426,7 +600,9 @@ long paillier_encrypt_openssl_internal(const paillier_public_key_t *key, BIGNUM // Verify that r E Zn* if (is_coprime_fast(r, key->n, ctx) != 1) - return -PAILLIER_ERROR_INVALID_RANDOMNESS; + { + return PAILLIER_ERROR_INVALID_RANDOMNESS; + } BN_CTX_start(ctx); @@ -434,52 +610,70 @@ long paillier_encrypt_openssl_internal(const paillier_public_key_t *key, BIGNUM BIGNUM *tmp2 = BN_CTX_get(ctx); if (!tmp1 || !tmp2) + { goto cleanup; + } // Compute ciphertext = g^plaintext*r^n mod n^2 // as will select g=n+1 ciphertext = (1+n*plaintext)*r^n mod n^2, see https://en.wikipedia.org/wiki/Paillier_cryptosystem if (!BN_mul(tmp1, key->n, plaintext, ctx)) + { goto cleanup; + } if (!BN_add_word(tmp1, 1)) + { goto cleanup; + } if (!BN_mod_exp(tmp2, r, key->n, key->n2, ctx)) + { goto cleanup; - + } if (!BN_mod_mul(ciphertext, tmp1, tmp2, key->n2, ctx)) + { goto cleanup; + } - ret = 0; + ret = PAILLIER_SUCCESS; cleanup: - if (ret) - ret = ERR_peek_error(); + if (-1 == ret) + { + ret = ERR_get_error() * -1; + } BN_CTX_end(ctx); return ret; } -static inline uint64_t encrypt_openssl(const paillier_public_key_t *key, BIGNUM *ciphertext, const BIGNUM *plaintext, BN_CTX *ctx) +static inline long encrypt_openssl(const paillier_public_key_t *key, BIGNUM *ciphertext, const BIGNUM *plaintext, BN_CTX *ctx) { - int ret = -1; + long ret = -1; BN_CTX_start(ctx); BIGNUM *r = BN_CTX_get(ctx); if (!r) - ret = ERR_peek_error(); + { + ret = ERR_get_error() * -1; + } else { do { if (!BN_rand_range(r, key->n)) + { + ret = ERR_get_error() * -1; break; + } + ret = paillier_encrypt_openssl_internal(key, ciphertext, r, plaintext, ctx); - } - while (ret == -PAILLIER_ERROR_INVALID_RANDOMNESS); + + } while (ret == PAILLIER_ERROR_INVALID_RANDOMNESS); } BN_CTX_end(ctx); + return ret; } @@ -491,7 +685,9 @@ long paillier_decrypt_openssl_internal(const paillier_private_key_t *key, const BIGNUM *tmp = BN_CTX_get(ctx); if (!tmp) + { goto cleanup; + } // verify that ciphertext and n are coprime if (is_coprime_fast(ciphertext, key->pub.n, ctx) != 1) @@ -502,16 +698,30 @@ long paillier_decrypt_openssl_internal(const paillier_private_key_t *key, const // Compute the plaintext = L(ciphertext^lamda mod n2)*mu mod n if (!BN_mod_exp(tmp, ciphertext, key->lamda, key->pub.n2, ctx)) + { goto cleanup; - if (L(tmp, tmp, key->pub.n, ctx) != 0) + } + + ret = L(tmp, tmp, key->pub.n, ctx); + if (ret != PAILLIER_SUCCESS) + { goto cleanup; + } + + ret = -1; //revet to openssl error + if (!BN_mod_mul(plaintext, tmp, key->mu, key->pub.n, ctx)) + { goto cleanup; + } + + ret = PAILLIER_SUCCESS; - ret = 0; cleanup: - if (ret) - ret = ERR_peek_error(); + if (-1 != ret) + { + ret = ERR_get_error() * -1; + } BN_CTX_end(ctx); return ret; @@ -520,42 +730,61 @@ long paillier_decrypt_openssl_internal(const paillier_private_key_t *key, const long paillier_encrypt(const paillier_public_key_t *key, const uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint32_t ciphertext_len, uint32_t *ciphertext_real_len) { long ret = -1; - int len; + int len = 0; + BIGNUM *msg = NULL, *c = NULL; + BN_CTX *ctx = NULL; + if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!plaintext || plaintext_len > (uint32_t)BN_num_bytes(key->n)) + { return PAILLIER_ERROR_INVALID_PLAIN_TEXT; + } + if (ciphertext_real_len) + { *ciphertext_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!ciphertext || ciphertext_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } - BN_CTX *ctx = NULL; - BIGNUM *msg, *c; - - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + ctx = BN_CTX_new(); + if (!ctx) + { + goto cleanup; + } BN_CTX_start(ctx); msg = BN_CTX_get(ctx); + c = BN_CTX_get(ctx); + if (!c || !msg) + { + goto cleanup; + } - if (!msg || !BN_bin2bn(plaintext, plaintext_len, msg)) + if (!BN_bin2bn(plaintext, plaintext_len, msg)) + { goto cleanup; + } if (BN_cmp(msg, key->n) >= 0) { // plaintext not in n - BN_CTX_end(ctx); - BN_CTX_free(ctx); - return PAILLIER_ERROR_INVALID_PLAIN_TEXT; + ret = PAILLIER_ERROR_INVALID_PLAIN_TEXT; + goto cleanup; } - c = BN_CTX_get(ctx); - if (!c) - goto cleanup; ret = encrypt_openssl(key, c, msg, ctx); - if (ret) + if (PAILLIER_SUCCESS != ret) + { goto cleanup; + } len = BN_bn2bin(c, ciphertext); if (len <= 0) @@ -565,13 +794,15 @@ long paillier_encrypt(const paillier_public_key_t *key, const uint8_t *plaintext } if (ciphertext_real_len) + { *ciphertext_real_len = len; - if (len) - ret = PAILLIER_SUCCESS; + } cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } if (ctx) { @@ -586,60 +817,81 @@ long paillier_encrypt_to_ciphertext(const paillier_public_key_t *key, const uint long ret = -1; paillier_ciphertext_t *c = NULL; BN_CTX *ctx = NULL; - BIGNUM *msg; + BIGNUM *msg = NULL; if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!plaintext || plaintext_len > (uint32_t)BN_num_bytes(key->n)) + { return PAILLIER_ERROR_INVALID_PLAIN_TEXT; + } if (!ciphertext) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } c = (paillier_ciphertext_t*)calloc(1, sizeof(paillier_ciphertext_t)); if (!c) + { return PAILLIER_ERROR_OUT_OF_MEMORY; + } if ((c->ciphertext = BN_new()) == NULL) + { goto cleanup; + } + if ((c->r = BN_new()) == NULL) + { goto cleanup; + } if ((ctx = BN_CTX_new()) == NULL) + { goto cleanup; + } BN_CTX_start(ctx); msg = BN_CTX_get(ctx); if (!msg || !BN_bin2bn(plaintext, plaintext_len, msg)) + { goto cleanup; + } if (BN_cmp(msg, key->n) >= 0) { // plaintext not in n - BN_CTX_end(ctx); - BN_CTX_free(ctx); - paillier_free_ciphertext(c); - return PAILLIER_ERROR_INVALID_PLAIN_TEXT; + ret = PAILLIER_ERROR_INVALID_PLAIN_TEXT; + goto cleanup; } do { if (!BN_rand_range(c->r, key->n)) + { + ret = -1; // reset ret so open ssl error will be fetched break; + } + ret = paillier_encrypt_openssl_internal(key, c->ciphertext, c->r, msg, ctx); - } - while (ret == -PAILLIER_ERROR_INVALID_RANDOMNESS); + } while (ret == PAILLIER_ERROR_INVALID_RANDOMNESS); - if (ret) + if (PAILLIER_SUCCESS != ret) + { goto cleanup; + } *ciphertext = c; c = NULL; - ret = PAILLIER_SUCCESS; cleanup: - if (ret > 0) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } if (ctx) { @@ -653,32 +905,50 @@ long paillier_encrypt_to_ciphertext(const paillier_public_key_t *key, const uint long paillier_encrypt_integer(const paillier_public_key_t *key, uint64_t plaintext, uint8_t *ciphertext, uint32_t ciphertext_len, uint32_t *ciphertext_real_len) { long ret = -1; - int len; + int len = 0; + BN_CTX *ctx = NULL; + BIGNUM *msg = NULL, *c = NULL; + if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (ciphertext_real_len) + { *ciphertext_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!ciphertext || ciphertext_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } - BN_CTX *ctx = NULL; - BIGNUM *msg, *c; - - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + ctx = BN_CTX_new(); + if (!ctx) + { + goto cleanup; + } BN_CTX_start(ctx); msg = BN_CTX_get(ctx); + c = BN_CTX_get(ctx); - if (!msg || !BN_set_word(msg, plaintext)) + if (!msg || !c) + { goto cleanup; + } - c = BN_CTX_get(ctx); - if (!c) + if (!BN_set_word(msg, plaintext)) + { goto cleanup; + } + ret = encrypt_openssl(key, c, msg, ctx); - if (ret) + if (PAILLIER_SUCCESS != ret) + { goto cleanup; + } len = BN_bn2bin(c, ciphertext); if (len <= 0) @@ -688,13 +958,15 @@ long paillier_encrypt_integer(const paillier_public_key_t *key, uint64_t plainte } if (ciphertext_real_len) + { *ciphertext_real_len = len; - if (len) - ret = 0; + } cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } if (ctx) { @@ -707,43 +979,64 @@ long paillier_encrypt_integer(const paillier_public_key_t *key, uint64_t plainte long paillier_decrypt(const paillier_private_key_t *key, const uint8_t *ciphertext, uint32_t ciphertext_len, uint8_t *plaintext, uint32_t plaintext_len, uint32_t *plaintext_real_len) { - long ret = PAILLIER_ERROR_OUT_OF_MEMORY; - int len; + long ret = -1; + int len = 0; + + BIGNUM *msg = NULL, *c = NULL; + BN_CTX *ctx = NULL; + if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!ciphertext || ciphertext_len > (uint32_t)BN_num_bytes(key->pub.n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (plaintext_real_len) + { *plaintext_real_len = (uint32_t)BN_num_bytes(key->pub.n); + } + if (!plaintext || plaintext_len < (uint32_t)BN_num_bytes(key->pub.n)) + { return PAILLIER_ERROR_INVALID_PLAIN_TEXT; + } - BN_CTX *ctx = NULL; - BIGNUM *msg, *c; - - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); c = BN_CTX_get(ctx); + msg = BN_CTX_get(ctx); + + if (!c || !msg) + { + goto cleanup; + } - if (!c || !BN_bin2bn(ciphertext, ciphertext_len, c)) + if (!BN_bin2bn(ciphertext, ciphertext_len, c)) + { goto cleanup; + } if (BN_cmp(c, key->pub.n2) >= 0) { // ciphertext not in n^2 - BN_CTX_end(ctx); - BN_CTX_free(ctx); - return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + ret = PAILLIER_ERROR_INVALID_CIPHER_TEXT; + goto cleanup; } - msg = BN_CTX_get(ctx); - if (!c) - goto cleanup; ret = paillier_decrypt_openssl_internal(key, c, msg, ctx); - if (ret) + if (PAILLIER_SUCCESS != ret) + { goto cleanup; + } len = BN_bn2bin(msg, plaintext); if (len <= 0) @@ -753,13 +1046,15 @@ long paillier_decrypt(const paillier_private_key_t *key, const uint8_t *cipherte } if (plaintext_real_len) + { *plaintext_real_len = len; - if (len) - ret = 0; + } cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } if (ctx) { @@ -772,53 +1067,73 @@ long paillier_decrypt(const paillier_private_key_t *key, const uint8_t *cipherte long paillier_decrypt_integer(const paillier_private_key_t *key, const uint8_t *ciphertext, uint32_t ciphertext_len, uint64_t *plaintext) { + + long ret = -1; + BIGNUM *msg = NULL, *c = NULL; + BN_CTX *ctx = NULL; + if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (ciphertext_len > (uint32_t)BN_num_bytes(key->pub.n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (!plaintext) + { return PAILLIER_ERROR_INVALID_PLAIN_TEXT; - long ret = -1; - int openssl_error = 1; - BN_CTX *ctx = NULL; - BIGNUM *msg, *c; + } - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); + c = BN_CTX_get(ctx); + msg = BN_CTX_get(ctx); - if (!c || !BN_bin2bn(ciphertext, ciphertext_len, c)) + if (!c || !msg) + { + goto cleanup; + } + + if (!BN_bin2bn(ciphertext, ciphertext_len, c)) + { goto cleanup; + } if (BN_cmp(c, key->pub.n2) >= 0) { // ciphertext not in n^2 ret = PAILLIER_ERROR_INVALID_CIPHER_TEXT; - openssl_error = 0; goto cleanup; } - msg = BN_CTX_get(ctx); - if (!c) - goto cleanup; ret = paillier_decrypt_openssl_internal(key, c, msg, ctx); - if (ret) + if (PAILLIER_SUCCESS != ret) + { goto cleanup; + } if ((uint32_t)BN_num_bytes(msg) > sizeof(*plaintext)) { ret = PAILLIER_ERROR_INVALID_PLAIN_TEXT; - openssl_error = 0; goto cleanup; } + *plaintext = BN_get_word(msg); - ret = 0; cleanup: - if (ret && openssl_error) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } if (ctx) { @@ -840,27 +1155,51 @@ long paillier_add(const paillier_public_key_t *key, const uint8_t *a_ciphertext, int len = 0; if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!a_ciphertext || a_ciphertext_len > (uint32_t)BN_num_bytes(key->n2) || !b_ciphertext || b_ciphertext_len > (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (result_real_len) + { *result_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!result || result_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + } + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); a = BN_CTX_get(ctx); - if (!a || !BN_bin2bn(a_ciphertext, a_ciphertext_len, a)) - goto cleanup; b = BN_CTX_get(ctx); - if (!b || !BN_bin2bn(b_ciphertext, b_ciphertext_len, b)) - goto cleanup; res = BN_CTX_get(ctx); - if (!res) + + if (!a || !b || !res) + { goto cleanup; + } + + if (!BN_bin2bn(a_ciphertext, a_ciphertext_len, a)) + { + goto cleanup; + } + + if (!BN_bin2bn(b_ciphertext, b_ciphertext_len, b)) + { + goto cleanup; + } // verify that a_ciphertext and b_ciphertext are coprime to n if (is_coprime_fast(a, key->n, ctx) != 1 || @@ -871,7 +1210,10 @@ long paillier_add(const paillier_public_key_t *key, const uint8_t *a_ciphertext, } if (!BN_mod_mul(res, a, b, key->n2, ctx)) + { goto cleanup; + } + len = BN_bn2bin(res, result); if (len <= 0) @@ -881,13 +1223,18 @@ long paillier_add(const paillier_public_key_t *key, const uint8_t *a_ciphertext, } if (result_real_len) + { *result_real_len = len; - if (len) - ret = 0; + } + + ret = PAILLIER_SUCCESS; cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } + if (ctx) { BN_CTX_end(ctx); @@ -906,26 +1253,49 @@ long paillier_add_integer(const paillier_public_key_t *key, const uint8_t *a_cip int len = 0; if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!a_ciphertext || a_ciphertext_len > (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (result_real_len) + { *result_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!result || result_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + } + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); bn_a = BN_CTX_get(ctx); - if (!bn_a || !BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) - goto cleanup; bn_b = BN_CTX_get(ctx); - if (!bn_b || !BN_set_word(bn_b, b)) - goto cleanup; res = BN_CTX_get(ctx); - if (!res) + if (!bn_a || !bn_b || !res) + { + goto cleanup; + } + + if (!BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) + { goto cleanup; + } + + + if (!BN_set_word(bn_b, b)) + { + goto cleanup; + } // verify that a_ciphertext and n are coprime if (is_coprime_fast(bn_a, key->n, ctx) != 1) @@ -935,11 +1305,18 @@ long paillier_add_integer(const paillier_public_key_t *key, const uint8_t *a_cip } ret = encrypt_openssl(key, res, bn_b, ctx); - if (ret) + if (PAILLIER_SUCCESS != ret) + { goto cleanup; + } + + ret = -1; //reset ret so next open ssl error would be logged. if (!BN_mod_mul(res, bn_a, res, key->n2, ctx)) + { goto cleanup; + } + len = BN_bn2bin(res, result); if (len <= 0) @@ -949,13 +1326,18 @@ long paillier_add_integer(const paillier_public_key_t *key, const uint8_t *a_cip } if (result_real_len) + { *result_real_len = len; - if (len) - ret = 0; + } + + ret = PAILLIER_SUCCESS; cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } + if (ctx) { BN_CTX_end(ctx); @@ -975,27 +1357,50 @@ long paillier_sub(const paillier_public_key_t *key, const uint8_t *a_ciphertext, int len = 0; if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!a_ciphertext || a_ciphertext_len > (uint32_t)BN_num_bytes(key->n2) || !b_ciphertext || b_ciphertext_len > (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (result_real_len) + { *result_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!result || result_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + } + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); a = BN_CTX_get(ctx); - if (!a || !BN_bin2bn(a_ciphertext, a_ciphertext_len, a)) - goto cleanup; b = BN_CTX_get(ctx); - if (!b || !BN_bin2bn(b_ciphertext, b_ciphertext_len, b)) - goto cleanup; res = BN_CTX_get(ctx); - if (!res) + if (!a || !b || !res) + { goto cleanup; + } + + if (!BN_bin2bn(a_ciphertext, a_ciphertext_len, a)) + { + goto cleanup; + } + + if (!BN_bin2bn(b_ciphertext, b_ciphertext_len, b)) + { + goto cleanup; + } // verify that a_ciphertext and b_ciphertext are coprime to n if (is_coprime_fast(a, key->n, ctx) != 1 || @@ -1006,7 +1411,9 @@ long paillier_sub(const paillier_public_key_t *key, const uint8_t *a_ciphertext, } if (!BN_mod_inverse(b, b, key->n2, ctx)) + { goto cleanup; + } if (!BN_mod_mul(res, a, b, key->n2, ctx)) goto cleanup; @@ -1019,13 +1426,18 @@ long paillier_sub(const paillier_public_key_t *key, const uint8_t *a_ciphertext, } if (result_real_len) + { *result_real_len = len; - if (len) - ret = 0; + } + + ret = PAILLIER_SUCCESS; cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } + if (ctx) { BN_CTX_end(ctx); @@ -1042,28 +1454,49 @@ long paillier_sub_integer(const paillier_public_key_t *key, const uint8_t *a_cip BIGNUM *res = NULL; long ret = -1; int len = 0; - if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!a_ciphertext || a_ciphertext_len > (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } if (result_real_len) + { *result_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!result || result_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + } + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } + BN_CTX_start(ctx); bn_a = BN_CTX_get(ctx); - if (!bn_a || !BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) - goto cleanup; bn_b = BN_CTX_get(ctx); - if (!bn_b || !BN_set_word(bn_b, b)) - goto cleanup; res = BN_CTX_get(ctx); - if (!res) + if (!bn_a || !bn_b || !res) + { goto cleanup; + } + if (!BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) + { + goto cleanup; + } + + if (!BN_set_word(bn_b, b)) + { + goto cleanup; + } + // verify that a_ciphertext and n are coprime if (is_coprime_fast(bn_a, key->n, ctx) != 1) @@ -1074,13 +1507,21 @@ long paillier_sub_integer(const paillier_public_key_t *key, const uint8_t *a_cip ret = encrypt_openssl(key, res, bn_b, ctx); if (ret) + { goto cleanup; + } + + ret = -1; //reset ret so new open ssl errors could be logged if (!BN_mod_inverse(res, res, key->n2, ctx)) + { goto cleanup; + } if (!BN_mod_mul(res, bn_a, res, key->n2, ctx)) + { goto cleanup; + } len = BN_bn2bin(res, result); if (len <= 0) @@ -1090,13 +1531,18 @@ long paillier_sub_integer(const paillier_public_key_t *key, const uint8_t *a_cip } if (result_real_len) + { *result_real_len = len; - if (len) - ret = 0; + } + + ret = PAILLIER_SUCCESS; cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } + if (ctx) { BN_CTX_end(ctx); @@ -1114,31 +1560,56 @@ long paillier_mul(const paillier_public_key_t *key, const uint8_t *a_ciphertext, BIGNUM *res = NULL; long ret = -1; int len = 0; - if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!a_ciphertext || a_ciphertext_len > (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (!b_plaintext || b_plaintext_len > (uint32_t)BN_num_bytes(key->n)) + { return PAILLIER_ERROR_INVALID_PLAIN_TEXT; + } if (result_real_len) + { *result_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!result || result_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + } + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); bn_a = BN_CTX_get(ctx); - if (!bn_a || !BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) - goto cleanup; bn_b = BN_CTX_get(ctx); - if (!bn_b || !BN_bin2bn(b_plaintext, b_plaintext_len, bn_b)) - goto cleanup; res = BN_CTX_get(ctx); - if (!res) + + if (!bn_a || !bn_b || !res) + { + goto cleanup; + } + + if (!BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) + { + goto cleanup; + } + + if (!BN_bin2bn(b_plaintext, b_plaintext_len, bn_b)) + { goto cleanup; + } // verify that a_ciphertext and n are coprime if (is_coprime_fast(bn_a, key->n, ctx) != 1) @@ -1148,7 +1619,9 @@ long paillier_mul(const paillier_public_key_t *key, const uint8_t *a_ciphertext, } if (!BN_mod_exp(res, bn_a, bn_b, key->n2, ctx)) + { goto cleanup; + } len = BN_bn2bin(res, result); if (len <= 0) @@ -1158,13 +1631,18 @@ long paillier_mul(const paillier_public_key_t *key, const uint8_t *a_ciphertext, } if (result_real_len) + { *result_real_len = len; - if (len) - ret = 0; + } + + ret = PAILLIER_SUCCESS; cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } + if (ctx) { BN_CTX_end(ctx); @@ -1183,26 +1661,49 @@ long paillier_mul_integer(const paillier_public_key_t *key, const uint8_t *a_cip int len = 0; if (!key) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!a_ciphertext || a_ciphertext_len > (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (result_real_len) + { *result_real_len = (uint32_t)BN_num_bytes(key->n2); + } + if (!result || result_len < (uint32_t)BN_num_bytes(key->n2)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + } + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); bn_a = BN_CTX_get(ctx); - if (!bn_a || !BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) - goto cleanup; bn_b = BN_CTX_get(ctx); - if (!bn_b || !BN_set_word(bn_b, b)) - goto cleanup; res = BN_CTX_get(ctx); - if (!res) + if (!bn_a || !bn_b || !res) + { goto cleanup; + } + + if (!BN_bin2bn(a_ciphertext, a_ciphertext_len, bn_a)) + { + goto cleanup; + } + + if (!BN_set_word(bn_b, b)) + { + goto cleanup; + } // verify that a_ciphertext and n are coprime if (is_coprime_fast(bn_a, key->n, ctx) != 1) @@ -1212,7 +1713,10 @@ long paillier_mul_integer(const paillier_public_key_t *key, const uint8_t *a_cip } if (!BN_mod_exp(res, bn_a, bn_b, key->n2, ctx)) + { goto cleanup; + } + len = BN_bn2bin(res, result); if (len <= 0) @@ -1222,13 +1726,19 @@ long paillier_mul_integer(const paillier_public_key_t *key, const uint8_t *a_cip } if (result_real_len) + { *result_real_len = len; - if (len) - ret = 0; + } + + + ret = PAILLIER_SUCCESS; cleanup: - if (ret) + if (-1 == ret) + { ret = ERR_get_error() * -1; + } + if (ctx) { BN_CTX_end(ctx); @@ -1240,13 +1750,25 @@ long paillier_mul_integer(const paillier_public_key_t *key, const uint8_t *a_cip long paillier_get_ciphertext(const paillier_ciphertext_t *ciphertext_object, uint8_t *ciphertext, uint32_t ciphertext_len, uint32_t *ciphertext_real_len) { if (!ciphertext_object) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (!ciphertext && ciphertext_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + if (ciphertext_real_len) + { *ciphertext_real_len = (uint32_t)BN_num_bytes(ciphertext_object->ciphertext); + } + if (!ciphertext || ciphertext_len < (uint32_t)BN_num_bytes(ciphertext_object->ciphertext)) + { return PAILLIER_ERROR_INVALID_CIPHER_TEXT; + } + if (BN_bn2bin(ciphertext_object->ciphertext, ciphertext) <= 0) { diff --git a/src/common/crypto/paillier/paillier_zkp.c b/src/common/crypto/paillier/paillier_zkp.c index 6c5524b..3b3f263 100644 --- a/src/common/crypto/paillier/paillier_zkp.c +++ b/src/common/crypto/paillier/paillier_zkp.c @@ -5,10 +5,15 @@ #include #include +#include #define FACTORIZANTION_ZKP_K 10 #define COPRIME_ZKP_K 16 -#define PAILLIER_BLUM_STATISTICAL_SECURITY 80 +#define PAILLIER_BLUM_STATISTICAL_SECURITY 80 //this is the original security actually used by CMP + +// this is the minimal required security +// can be used if (pub->n mod 4) == 1 +#define PAILLIER_BLUM_STATISTICAL_SECURITY_MINIMAL_REQUIRED 64 #define FACTORIZANTION_ZKP_SALT "factorization zkpok" #define COPRIME_ZKP_SALT "coprime zkp" @@ -26,12 +31,13 @@ typedef struct static BIGNUM* deterministic_rand(const sha256_md_t seed, uint32_t n_len, BIGNUM *bn_r, sha256_md_t *out_md) { SHA512_CTX sha512_ctx; - uint8_t *r; - uint8_t *r_ptr; - - r = (uint8_t*)malloc(n_len + SHA512_DIGEST_LENGTH); + uint8_t *r_ptr = NULL; + uint8_t *r = (uint8_t*)malloc(n_len + SHA512_DIGEST_LENGTH); if (!r) + { return NULL; + } + memcpy(r, seed, sizeof(sha256_md_t)); r_ptr = r; @@ -42,8 +48,12 @@ static BIGNUM* deterministic_rand(const sha256_md_t seed, uint32_t n_len, BIGNUM SHA512_Final(r_ptr, &sha512_ctx); r_ptr += PAILLIER_SHA256_LEN; } + if (out_md) + { memcpy(*out_md, r_ptr, sizeof(sha256_md_t)); + } + BN_bin2bn(r, n_len, bn_r); free(r); return bn_r; @@ -51,22 +61,31 @@ static BIGNUM* deterministic_rand(const sha256_md_t seed, uint32_t n_len, BIGNUM static inline long update_with_bignum(SHA256_CTX *ctx, const BIGNUM *bn) { - uint8_t *n = NULL; uint32_t len = BN_num_bytes(bn); - n = (uint8_t*)malloc(len); + uint8_t *n = (uint8_t*)malloc(len); if (!n) + { return PAILLIER_ERROR_OUT_OF_MEMORY; + } + if (BN_bn2bin(bn, n) <= 0) // should never happen { free(n); - return ERR_peek_error() * -1; + return -1; //so that the caller would know to read OpenSSL error } + SHA256_Update(ctx, n, len); free(n); return PAILLIER_SUCCESS; } -long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, const uint8_t *aad, uint32_t aad_len, uint8_t x[PAILLIER_SHA256_LEN], uint8_t *y, uint32_t y_len, uint32_t *y_real_len) +long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, + const uint8_t *aad, + uint32_t aad_len, + uint8_t x[PAILLIER_SHA256_LEN], + uint8_t *y, + uint32_t y_len, + uint32_t *y_real_len) { BN_CTX *ctx = NULL; BN_MONT_CTX *mont = NULL; @@ -82,49 +101,74 @@ long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, c long ret = -1; if (!priv) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!aad && aad_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } if (!x) + { return PAILLIER_ERROR_INVALID_PARAM; + } if (!y && y_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } n_len = BN_num_bytes(priv->pub.n); assert(n_len % PAILLIER_SHA256_LEN == 0); if (n_len % PAILLIER_SHA256_LEN != 0) + { return PAILLIER_ERROR_INVALID_KEY; + } - if ((ctx = BN_CTX_new()) == NULL) + ctx = BN_CTX_new(); + if (!ctx) + { return ERR_get_error() * -1; + } BN_CTX_start(ctx); - if (!(A = BN_CTX_get(ctx))) + A = BN_CTX_get(ctx); + r = BN_CTX_get(ctx); + e = BN_CTX_get(ctx); + bn_y = BN_CTX_get(ctx); + z = BN_CTX_get(ctx); + + if (!A || !r || !e || !bn_y || !z) + { + goto cleanup; + } + + mont = BN_MONT_CTX_new(); + if (!mont) + { goto cleanup; + } if (!BN_rshift1(A, priv->pub.n)) + { goto cleanup; + } if (y_real_len) + { *y_real_len = BN_num_bytes(A); + } + if (!y || y_len < (uint32_t)BN_num_bytes(A)) { ret = PAILLIER_ERROR_BUFFER_TOO_SHORT; goto cleanup; } - if (!(r = BN_CTX_get(ctx))) - goto cleanup; - if (!(e = BN_CTX_get(ctx))) - goto cleanup; - if (!(bn_y = BN_CTX_get(ctx))) - goto cleanup; - if (!(z = BN_CTX_get(ctx))) - goto cleanup; - if (!BN_rand_range(r, A)) + { goto cleanup; + } n = (uint8_t*)malloc(n_len); if (!n) @@ -132,8 +176,11 @@ long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, c ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; } + if (!BN_bn2bin(priv->pub.n, n)) + { goto cleanup; + } tmp = (uint8_t*)malloc(n_len); if (!tmp) @@ -146,7 +193,9 @@ long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, c SHA256_Update(&sha256_ctx, FACTORIZANTION_ZKP_SALT, sizeof(FACTORIZANTION_ZKP_SALT)); SHA256_Update(&sha256_ctx, n, n_len); if (aad) + { SHA256_Update(&sha256_ctx, aad, aad_len); + } SHA256_Final(seed, &sha256_ctx); SHA512_Init(&sha512_ctx); @@ -154,11 +203,11 @@ long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, c SHA512_Update(&sha512_ctx, n, n_len); - if (!(mont = BN_MONT_CTX_new())) - goto cleanup; if (!BN_MONT_CTX_set(mont, priv->pub.n, ctx)) + { goto cleanup; + } for (size_t i = 0; i < FACTORIZANTION_ZKP_K; ++i) { @@ -168,12 +217,18 @@ long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, c } while (BN_cmp(z, priv->pub.n) >= 0); if (!BN_bn2bin(z, tmp)) + { goto cleanup; + } SHA512_Update(&sha512_ctx, tmp, BN_num_bytes(z)); if (!BN_mod_exp_mont(z, z, r, priv->pub.n, ctx, mont)) + { goto cleanup; + } if (!BN_bn2bin(z, tmp)) + { goto cleanup; + } SHA256_Update(&sha256_ctx, tmp, BN_num_bytes(z)); } SHA256_Final(x, &sha256_ctx); @@ -181,32 +236,49 @@ long paillier_generate_factorization_zkpok(const paillier_private_key_t *priv, c SHA512_Final(sha512_md, &sha512_ctx); if (!BN_bin2bn(sha512_md, sizeof(sha512_md), e)) + { goto cleanup; + } if (!BN_usub(bn_y, priv->pub.n, priv->lamda)) + { goto cleanup; + } if (!BN_mul(bn_y, bn_y, e, ctx)) + { goto cleanup; + } if (!BN_mod_add(bn_y, bn_y, r, A, ctx)) + { goto cleanup; + } if (BN_bn2binpad(bn_y, y, BN_num_bytes(A)) <= 0) + { goto cleanup; + } ret = PAILLIER_SUCCESS; cleanup: if (ret < 0) + { ret = ERR_get_error() * -1; + } free(n); free(tmp); - BN_MONT_CTX_free(mont); + BN_MONT_CTX_free(mont); //it is OK to call it on NULL BN_CTX_end(ctx); BN_CTX_free(ctx); return ret; } -long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, const uint8_t *aad, uint32_t aad_len, const uint8_t x[PAILLIER_SHA256_LEN], const uint8_t *y, uint32_t y_len) +long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, + const uint8_t *aad, + uint32_t aad_len, + const uint8_t x[PAILLIER_SHA256_LEN], + const uint8_t *y, + uint32_t y_len) { BN_CTX *ctx = NULL; BN_MONT_CTX *mont = NULL; @@ -224,30 +296,51 @@ long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, const long ret = -1; if (!pub) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!aad && aad_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } if (!x) + { return PAILLIER_ERROR_INVALID_PARAM; + } if (!y || !y_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } n_len = BN_num_bytes(pub->n); assert(n_len % PAILLIER_SHA256_LEN == 0); if (n_len % PAILLIER_SHA256_LEN != 0) + { return PAILLIER_ERROR_INVALID_KEY; + } - if ((ctx = BN_CTX_new()) == NULL) + ctx = BN_CTX_new(); + if (!ctx) + { return ERR_get_error() * -1; + } BN_CTX_start(ctx); - if (!(e = BN_CTX_get(ctx))) - goto cleanup; - if (!(bn_y = BN_CTX_get(ctx))) + e = BN_CTX_get(ctx); + bn_y = BN_CTX_get(ctx); + exp = BN_CTX_get(ctx); + + if (!e || !bn_y || !exp) + { goto cleanup; - if (!(exp = BN_CTX_get(ctx))) + } + + mont = BN_MONT_CTX_new(); + if (!mont) + { goto cleanup; + } n = (uint8_t*)malloc(n_len); if (!n) @@ -255,8 +348,11 @@ long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, const ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; } + if (!BN_bn2bin(pub->n, n)) + { goto cleanup; + } tmp = (uint8_t*)malloc(n_len); if (!tmp) @@ -269,17 +365,21 @@ long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, const SHA256_Update(&sha256_ctx, FACTORIZANTION_ZKP_SALT, sizeof(FACTORIZANTION_ZKP_SALT)); SHA256_Update(&sha256_ctx, n, n_len); if (aad) + { SHA256_Update(&sha256_ctx, aad, aad_len); + } SHA256_Final(seed, &sha256_ctx); - SHA512_Init(&sha512_ctx); + SHA512_Init(&sha512_ctx); //reinitialize the context for reuse SHA512_Update(&sha512_ctx, n, n_len); for (size_t i = 0; i < FACTORIZANTION_ZKP_K; ++i) { if (!(z[i] = BN_CTX_get(ctx))) + { goto cleanup; + } do { @@ -287,28 +387,40 @@ long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, const } while (BN_cmp(z[i], pub->n) >= 0); if (!BN_bn2bin(z[i], tmp)) + { goto cleanup; + } SHA512_Update(&sha512_ctx, tmp, BN_num_bytes(z[i])); } SHA512_Update(&sha512_ctx, x, PAILLIER_SHA256_LEN); SHA512_Final(sha512_md, &sha512_ctx); if (!BN_bin2bn(sha512_md, sizeof(sha512_md), e)) + { goto cleanup; - - SHA256_Init(&sha256_ctx); + } if (!BN_bin2bn(y, y_len, bn_y)) + { goto cleanup; + } if (!BN_mul(exp, pub->n, e, ctx)) + { goto cleanup; + } if (!BN_sub(exp, exp, bn_y)) + { goto cleanup; + } if (!BN_lshift1(bn_y, bn_y)) + { goto cleanup; + } if (!BN_add_word(bn_y, 1)) + { goto cleanup; + } if (BN_cmp(bn_y, pub->n) >= 0) { @@ -316,35 +428,44 @@ long paillier_verify_factorization_zkpok(const paillier_public_key_t *pub, const goto cleanup; } - SHA256_Init(&sha256_ctx); + SHA256_Init(&sha256_ctx); //reinitialize the context for reuse - if (!(mont = BN_MONT_CTX_new())) - goto cleanup; if (!BN_MONT_CTX_set(mont, pub->n, ctx)) + { goto cleanup; + } for (size_t i = 0; i < FACTORIZANTION_ZKP_K; ++i) { if (!BN_mod_exp_mont(z[i], z[i], exp, pub->n, ctx, mont)) + { goto cleanup; + } if (!BN_mod_inverse(z[i], z[i], pub->n, ctx)) + { goto cleanup; + } if (!BN_bn2bin(z[i], tmp)) + { goto cleanup; + } SHA256_Update(&sha256_ctx, tmp, BN_num_bytes(z[i])); } + SHA256_Final(sha256_md, &sha256_ctx); - ret = memcmp(x, sha256_md, PAILLIER_SHA256_LEN) == 0 ? PAILLIER_SUCCESS : PAILLIER_ERROR_INVALID_PROOF; + ret = (memcmp(x, sha256_md, PAILLIER_SHA256_LEN) == 0) ? PAILLIER_SUCCESS : PAILLIER_ERROR_INVALID_PROOF; cleanup: if (ret < 0) + { ret = ERR_get_error() * -1; + } free(n); free(tmp); - BN_MONT_CTX_free(mont); + BN_MONT_CTX_free(mont); //It is OK to call it on NULL BN_CTX_end(ctx); BN_CTX_free(ctx); return ret; @@ -364,39 +485,67 @@ long paillier_generate_coprime_zkp(const paillier_private_key_t *priv, const uin long ret = -1; if (!priv) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!aad && aad_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + if (!y && y_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } n_len = BN_num_bytes(priv->pub.n); assert(n_len % PAILLIER_SHA256_LEN == 0); if (n_len % PAILLIER_SHA256_LEN != 0) + { return PAILLIER_ERROR_INVALID_KEY; + } if (y_real_len) + { *y_real_len = n_len * COPRIME_ZKP_K; + } + if (!y || y_len < n_len * COPRIME_ZKP_K) + { return PAILLIER_ERROR_BUFFER_TOO_SHORT; + } - if ((ctx = BN_CTX_new()) == NULL) + ctx = BN_CTX_new(); + if (!ctx) + { return ERR_get_error() * -1; + } BN_CTX_start(ctx); - if (!(x = BN_CTX_get(ctx))) - goto cleanup; - if (!(M = BN_CTX_get(ctx))) + x = BN_CTX_get(ctx); + M = BN_CTX_get(ctx); + tmp = BN_CTX_get(ctx); + + if (!x || !M || !tmp) + { goto cleanup; - if (!(tmp = BN_CTX_get(ctx))) + } + + mont = BN_MONT_CTX_new(); + if (!mont) + { goto cleanup; + } BN_set_flags(M, BN_FLG_CONSTTIME); if (!BN_mod_inverse(M, priv->pub.n, priv->lamda, ctx)) + { goto cleanup; + } n = (uint8_t*)malloc(n_len); if (!n) @@ -404,40 +553,53 @@ long paillier_generate_coprime_zkp(const paillier_private_key_t *priv, const uin ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; } + if (!BN_bn2bin(priv->pub.n, n)) + { goto cleanup; + } SHA256_Init(&sha256_ctx); SHA256_Update(&sha256_ctx, COPRIME_ZKP_SALT, sizeof(COPRIME_ZKP_SALT)); SHA256_Update(&sha256_ctx, n, n_len); if (aad) + { SHA256_Update(&sha256_ctx, aad, aad_len); + } SHA256_Final(seed, &sha256_ctx); free(n); n = NULL; - if (!(mont = BN_MONT_CTX_new())) - goto cleanup; if (!BN_MONT_CTX_set(mont, priv->pub.n, ctx)) + { goto cleanup; + } for (size_t i = 0; i < COPRIME_ZKP_K; ++i) { - int res; + int is_coprime_res; do { deterministic_rand(seed, n_len, x, &seed); - res = is_coprime_fast(x, priv->pub.n, ctx); - } while (res == 0); + is_coprime_res = is_coprime_fast(x, priv->pub.n, ctx); + } while (is_coprime_res == 0); - if (res == -1) + if (is_coprime_res == -1) + { goto cleanup; + } if (!BN_mod_exp_mont(tmp, x, M, priv->pub.n, ctx, mont)) + { goto cleanup; + } + if (BN_bn2binpad(tmp, y_ptr, n_len) < 0) + { goto cleanup; + } + y_ptr += n_len; } @@ -445,7 +607,10 @@ long paillier_generate_coprime_zkp(const paillier_private_key_t *priv, const uin cleanup: if (ret < 0) + { ret = ERR_get_error() * -1; + } + free(n); BN_MONT_CTX_free(mont); BN_CTX_end(ctx); @@ -467,24 +632,40 @@ long paillier_verify_coprime_zkp(const paillier_public_key_t *pub, const uint8_t long ret = -1; if (!pub) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!aad && aad_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + if (!y && y_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + n_len = BN_num_bytes(pub->n); assert(n_len % PAILLIER_SHA256_LEN == 0); if (n_len % PAILLIER_SHA256_LEN != 0) + { return PAILLIER_ERROR_INVALID_KEY; + } assert(y_len == n_len * COPRIME_ZKP_K); if (!y || y_len < n_len * COPRIME_ZKP_K) + { return PAILLIER_ERROR_INVALID_PARAM; + } - if ((ctx = BN_CTX_new()) == NULL) + ctx = BN_CTX_new(); + if (!ctx) + { return ERR_get_error() * -1; + } BN_CTX_start(ctx); @@ -494,15 +675,26 @@ long paillier_verify_coprime_zkp(const paillier_public_key_t *pub, const uint8_t goto cleanup; } - if (!(x = BN_CTX_get(ctx))) - goto cleanup; - if (!(bn_y = BN_CTX_get(ctx))) + x = BN_CTX_get(ctx); + bn_y = BN_CTX_get(ctx); + tmp = BN_CTX_get(ctx); + + if (!x || !bn_y || !tmp) + { goto cleanup; - if (!(tmp = BN_CTX_get(ctx))) + } + + mont = BN_MONT_CTX_new(); + if (!mont) + { goto cleanup; + } if (!BN_bin2bn(alpha_bin, alpha_bin_len, tmp)) + { goto cleanup; + } + if (is_coprime_fast(tmp, pub->n, ctx) != 1) { @@ -517,41 +709,56 @@ long paillier_verify_coprime_zkp(const paillier_public_key_t *pub, const uint8_t goto cleanup; } if (!BN_bn2bin(pub->n, n)) + { goto cleanup; + } + SHA256_Init(&sha256_ctx); SHA256_Update(&sha256_ctx, COPRIME_ZKP_SALT, sizeof(COPRIME_ZKP_SALT)); SHA256_Update(&sha256_ctx, n, n_len); if (aad) + { SHA256_Update(&sha256_ctx, aad, aad_len); + } SHA256_Final(seed, &sha256_ctx); free(n); n = NULL; - if (!(mont = BN_MONT_CTX_new())) - goto cleanup; - if (!BN_MONT_CTX_set(mont, pub->n, ctx)) + { goto cleanup; + } for (size_t i = 0; i < COPRIME_ZKP_K; ++i) { - int res; + int is_coprime_res; do { deterministic_rand(seed, n_len, x, &seed); - res = is_coprime_fast(x, pub->n, ctx); - } while (res == 0); + is_coprime_res = is_coprime_fast(x, pub->n, ctx); + } while (is_coprime_res == 0); - if (res == -1) + if (is_coprime_res == -1) + { goto cleanup; + } if (!BN_mod(x, x, pub->n, ctx)) + { goto cleanup; + } + if (!BN_bin2bn(y_ptr, n_len, bn_y)) + { goto cleanup; + } + if (!BN_mod_exp_mont(tmp, bn_y, pub->n, pub->n, ctx, mont)) + { goto cleanup; + } + if (BN_cmp(tmp, x) != 0) { ret = PAILLIER_ERROR_INVALID_PROOF; @@ -564,7 +771,9 @@ long paillier_verify_coprime_zkp(const paillier_public_key_t *pub, const uint8_t cleanup: if (ret < 0) + { ret = ERR_get_error() * -1; + } free(n); BN_MONT_CTX_free(mont); BN_CTX_end(ctx); @@ -576,14 +785,19 @@ static inline long init_paillier_blum_zkp(zkp_paillier_blum_modulus_proof_t *pro { proof->w = BN_CTX_get(ctx); if (!proof->w) + { return PAILLIER_ERROR_OUT_OF_MEMORY; + } + for (size_t i = 0; i < PAILLIER_BLUM_STATISTICAL_SECURITY; i++) { proof->x[i] = BN_CTX_get(ctx); proof->z[i] = BN_CTX_get(ctx); if (!proof->x[i] || !proof->z[i]) + { return PAILLIER_ERROR_OUT_OF_MEMORY; } + } return PAILLIER_SUCCESS; } @@ -621,19 +835,31 @@ static inline int deserialize_paillier_blum_zkp(zkp_paillier_blum_modulus_proof_ ptr += sizeof(uint32_t); if (n_len != proof_n_len) + { return 0; + } + if (!BN_bin2bn(ptr, n_len, proof->w)) + { return 0; + } + ptr += n_len; for (uint32_t i = 0; i < PAILLIER_BLUM_STATISTICAL_SECURITY; ++i) { if (!BN_bin2bn(ptr, n_len, proof->x[i])) + { return 0; + } + ptr += n_len; if (!BN_bin2bn(ptr, n_len, proof->z[i])) + { return 0; + } + ptr += n_len; proof->a[i] = *ptr++; proof->b[i] = *ptr++; @@ -641,6 +867,20 @@ static inline int deserialize_paillier_blum_zkp(zkp_paillier_blum_modulus_proof_ return 1; } +static inline uint8_t get_2bit_number(const uint8_t* array, const uint32_t i) +{ + // Calculate which byte the 2-bit number is in + const uint32_t byte_index = i / 4; + + // Calculate the bit position within that byte (0, 2, 4, or 6) + const uint32_t bit_position = (i % 4) * 2; + + // Extract the 2-bit number by shifting and masking + return (uint8_t)(array[byte_index] >> bit_position) & 0x03; +} + + + long paillier_generate_paillier_blum_zkp(const paillier_private_key_t *priv, const uint8_t *aad, uint32_t aad_len, uint8_t *serialized_proof, uint32_t proof_len, uint32_t *proof_real_len) { BN_CTX *ctx = NULL; @@ -659,30 +899,60 @@ long paillier_generate_paillier_blum_zkp(const paillier_private_key_t *priv, con uint32_t n_len; uint32_t needed_proof_len; + // since it is needed to randomly choose one of the 4 roots + // in a loop of PAILLIER_BLUM_STATISTICAL_SECURITY iterations + // there is a need for 2 * PAILLIER_BLUM_STATISTICAL_SECURITY bits. + // adding 7 will round up if PAILLIER_BLUM_STATISTICAL_SECURITY is not multiple of 8 + uint8_t random_bytes[(PAILLIER_BLUM_STATISTICAL_SECURITY * 2 + 7) / 8]; + long ret = -1; if (!priv) + { return PAILLIER_ERROR_INVALID_KEY; + } if (!aad && aad_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } if (!serialized_proof && proof_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + + //assume that (BN_mod_word(priv->p, 8) == 3 && BN_mod_word(priv->q, 8) == 7) needed_proof_len = paillier_blum_zkp_serialized_size(&priv->pub); if (proof_real_len) + { *proof_real_len = needed_proof_len; + } if (proof_len < needed_proof_len) + { return PAILLIER_ERROR_BUFFER_TOO_SHORT; + } + OPENSSL_cleanse(serialized_proof, proof_len); n_len = BN_num_bytes(priv->pub.n); - if ((ctx = BN_CTX_new()) == NULL) - return ERR_get_error() * -1; + + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } BN_CTX_start(ctx); - if (init_paillier_blum_zkp(&proof, ctx) != PAILLIER_SUCCESS) + ret = init_paillier_blum_zkp(&proof, ctx); + + if (ret != PAILLIER_SUCCESS) + { goto cleanup; + } + + //reset return value so if following statements fail a propper error would be reported + ret = -1; p_remainder = BN_CTX_get(ctx); q_remainder = BN_CTX_get(ctx); @@ -691,153 +961,312 @@ long paillier_generate_paillier_blum_zkp(const paillier_private_key_t *priv, con b = BN_CTX_get(ctx); correction = BN_CTX_get(ctx); if (!q_remainder || !p_remainder || !tmp || !a || !b || !correction) + { + ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; + } - // Generate w with (-1, 1) Jacobi signs wrt (p,q) by Chinese remainder theorem - // Satisfying w = -a^4 mod p and w = b^4 mod q for random a,b - if (!BN_mod_inverse(p_remainder, priv->p, priv->q, ctx)) - goto cleanup; - if (!BN_mod_inverse(q_remainder, priv->q, priv->p, ctx)) - goto cleanup; - if (!BN_mod_mul(p_remainder, p_remainder, priv->p, priv->pub.n, ctx)) - goto cleanup; - if (!BN_mod_mul(q_remainder, q_remainder, priv->q, priv->pub.n, ctx)) - goto cleanup; - if (!BN_rand_range(a, priv->p)) - goto cleanup; - if (!BN_rand_range(b, priv->q)) + n_inverse_mod_phi_n = BN_CTX_get(ctx); + if (!n_inverse_mod_phi_n) + { + ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; + } - // Compute correction as a mod p and b mod q, this will be the "QR-corrected" 4th root of w (later) - if (!BN_mod_mul(tmp, q_remainder, a, priv->pub.n, ctx)) - goto cleanup; - if (!BN_mod_mul(correction, p_remainder, b, priv->pub.n, ctx)) - goto cleanup; - if (!BN_mod_add_quick(correction, tmp, correction, priv->pub.n)) - goto cleanup; + y_mod_pq = BN_CTX_get(ctx); + p_4th_root = BN_CTX_get(ctx); + q_4th_root = BN_CTX_get(ctx); - // Set w = -a^4*q*q_inv_mod_p + b^4*p*p_inv_mod_q to satisfy above - // Notice correction^4 = a^4*q*q_inv_mod_p + b^4*p*p_inv_mod_q (this is "QR-corrected" w) - if (!BN_sqr(a, a, ctx)) - goto cleanup; - if (!BN_sqr(a, a, ctx)) + if (!y_mod_pq || !p_4th_root || !q_4th_root) + { + ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; - if (!BN_sqr(b, b, ctx)) + } + + y = BN_CTX_get(ctx); + if (!y) + { + ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; - if (!BN_sqr(b, b, ctx)) + } + + // Generate w with (-1, 1) Jacobi signs wrt (p,q) by Chinese remainder theorem + // Satisfying w = -a^4 mod p and w = b^4 mod q + // choose w to be 2^n and calculate a and b + + // calculate p_remainder and q_remainder wich will be used to quickly find value in mod n if we know + // the value in mod p and mod q using Chinese remainder theorem + if (!BN_mod_inverse(p_remainder, priv->p, priv->q, ctx)) // p_remainder = p^(-1) mod q + { goto cleanup; - if (!BN_mod_mul(a, q_remainder, a, priv->pub.n, ctx)) + } + if (!BN_mod_inverse(q_remainder, priv->q, priv->p, ctx)) // q_remainder = q^(-1) mod p + { goto cleanup; - if (!BN_mod_mul(b, p_remainder, b, priv->pub.n, ctx)) + } + + //since p and q are secret, their dericative require also const time calculations + BN_set_flags(p_remainder, BN_FLG_CONSTTIME); + BN_set_flags(q_remainder, BN_FLG_CONSTTIME); + + if (!BN_mod_mul(p_remainder, p_remainder, priv->p, priv->pub.n, ctx)) // p_remainder = (p^(-1) mod q) * p mod n + { goto cleanup; - if (!BN_mod_sub_quick(proof.w, b, a, priv->pub.n)) + } + if (!BN_mod_mul(q_remainder, q_remainder, priv->q, priv->pub.n, ctx)) // q_remainder = (q^(-1) mod p) * q mod n + { goto cleanup; + } + - n_inverse_mod_phi_n = BN_CTX_get(ctx); - if (!n_inverse_mod_phi_n) - goto cleanup; if (!BN_mod_inverse(n_inverse_mod_phi_n, priv->pub.n, priv->lamda, ctx)) // To compute z[i] + { goto cleanup; + } + + //since n_inverse_mod_phi_n is secret, it requires const time calculations + BN_set_flags(n_inverse_mod_phi_n, BN_FLG_CONSTTIME); // Taking each y[i] 4th root (by exponentation with p_exp_4th = ((p-1)/2)^2 mod (p -1) - double sqrt // Checking result^4 = y[i] or -y[i], which defines the legendre symbol p_minus_1 = BN_dup(priv->p); q_minus_1 = BN_dup(priv->q); if (!p_minus_1 || !q_minus_1) + { + ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; + } - if (!BN_sub_word(p_minus_1, 1)) - goto cleanup; - if (!BN_sub_word(q_minus_1, 1)) + //since p and q are primes, the are odd. Clearing last bit is same as doing minus 1 + // so p_minus_1 = p -1 , q_minus_1 = q - 1 + if (!BN_clear_bit(p_minus_1, 0) || !BN_clear_bit(q_minus_1, 0)) + { goto cleanup; + } + + // p-1 and q-1 are secret as well + BN_set_flags(p_minus_1, BN_FLG_CONSTTIME); + BN_set_flags(q_minus_1, BN_FLG_CONSTTIME); p_exp_4th = BN_dup(priv->p); q_exp_4th = BN_dup(priv->q); if (!p_exp_4th || !q_exp_4th) + { + ret = PAILLIER_ERROR_OUT_OF_MEMORY; goto cleanup; + } - if (!BN_add_word(p_exp_4th, 1)) + BN_set_flags(p_exp_4th, BN_FLG_CONSTTIME); + BN_set_flags(q_exp_4th, BN_FLG_CONSTTIME); + + + // Since p = 3[4], then ((p+1)/4)^2 mod (p-1) = (2/4)^2 mod (p-1) = 1/4 mod (p-1) + // So the exponent ((p+1)/4)^2 can be used to compute 4th root. + + if (!BN_add_word(p_exp_4th, 1)) //p_exp_4th = p + 1 + { goto cleanup; - if (!BN_rshift(p_exp_4th, p_exp_4th, 2)) + } + + if (!BN_rshift(p_exp_4th, p_exp_4th, 2)) //p_exp_4th = (p + 1) / 4 + { goto cleanup; - if (!BN_mod_sqr(p_exp_4th, p_exp_4th, p_minus_1, ctx)) + } + if (!BN_mod_sqr(p_exp_4th, p_exp_4th, p_minus_1, ctx)) // p_exp_4th = ((p + 1) / 4) ^ 2 mod (p -1) + { goto cleanup; + } - if (!BN_add_word(q_exp_4th, 1)) + if (!BN_add_word(q_exp_4th, 1)) // q_exp_4th = q + 1 + { goto cleanup; - if (!BN_rshift(q_exp_4th, q_exp_4th, 2)) + } + if (!BN_rshift(q_exp_4th, q_exp_4th, 2)) // q_exp_4th = (q + 1) / 4 + { goto cleanup; - if (!BN_mod_sqr(q_exp_4th, q_exp_4th, q_minus_1, ctx)) + } + if (!BN_mod_sqr(q_exp_4th, q_exp_4th, q_minus_1, ctx)) // q_exp_4th = ((q + 1) / 4) ^2 mod (q - 1) + { goto cleanup; + } + BN_clear_free(p_minus_1); BN_clear_free(q_minus_1); + p_minus_1 = NULL; q_minus_1 = NULL; - y_mod_pq = BN_CTX_get(ctx); - p_4th_root = BN_CTX_get(ctx); - q_4th_root = BN_CTX_get(ctx); - if (!y_mod_pq || !p_4th_root || !q_4th_root) + // We want to generate w such that (w|p) = -1 and (w|q) = 1 + // In the case where (p,q) = (3,7) mod 8, 2 will always be a non square mod p, and a square mod q, + // so fulfills the above condition. For the security proof to hold, though, we need to set w = 2^N [N]. + // Otherwise, we generate w with (-1, 1) Jacobi signs wrt (p,q) by Chinese remainder theorem + // Satisfying w = -a^4 mod p and w = b^4 mod q for random a,b + // set w to 2 and computer (w=2^n % n) + if (!BN_set_word(proof.w, 2) || !BN_mod_exp(proof.w, proof.w, priv->pub.n, priv->pub.n, ctx)) + { goto cleanup; + } + + // Compute correction as a mod p and b mod q, this will be the "QR-corrected" 4th root of w (later) + // It mainly means that + // | correction^4 = -w mod p + // | correction^4 = w mod q + // reminder: p_exp_4th = ((p + 1) / 4) ^ 2 mod (p - 1) which is (2/4) ^2 mod (p - 1) = 1/4 mod (p - 1) + // q_exp_4th = ((q + 1) / 4) ^ 2 mod (q - 1) which is (2/4) ^2 mod (q - 1) = 1/4 mod (q - 1) + // and we need to calculate w ^ 1/4 mod (N) + if (!BN_mod_exp(a, proof.w, p_exp_4th, priv->p, ctx) || // a = (w ^ ((2/4) ^2 mod (p - 1))) mod p + !BN_mod_exp(b, proof.w, q_exp_4th, priv->q, ctx)) // b = (w ^ ((2/4) ^2 mod (q - 1))) mod q + { + goto cleanup; + } + + //now we need to move from mod p and mod q into mod n + if (!BN_mod_mul(a, q_remainder, a, priv->pub.n, ctx)) + { + goto cleanup; + } + if (!BN_mod_mul(b, p_remainder, b, priv->pub.n, ctx)) + { + goto cleanup; + } + + if (!BN_mod_sub_quick(correction, b, a, priv->pub.n)) + { + goto cleanup; + } + + // Calculate seed for deterministric rand which in turn will be used for generating y values using hash of the current state. + // This comes instead of receiving y values from the other party SHA256_Init(&sha256_ctx); SHA256_Update(&sha256_ctx, PAILLIER_BLUM_ZKP_SALT, sizeof(PAILLIER_BLUM_ZKP_SALT)); if (aad) + { SHA256_Update(&sha256_ctx, aad, aad_len); + } + ret = update_with_bignum(&sha256_ctx, priv->pub.n); if (ret != PAILLIER_SUCCESS) + { goto cleanup; + } + ret = update_with_bignum(&sha256_ctx, proof.w); if (ret != PAILLIER_SUCCESS) + { goto cleanup; + } + SHA256_Final(seed, &sha256_ctx); + + //reset return value so if following statements fail a propper error would be reported ret = -1; - y = BN_CTX_get(ctx); - if (!y) + // The following randomization is needed for the security proof + if (RAND_bytes(random_bytes, sizeof(random_bytes)) != 1) + { goto cleanup; + } for (uint32_t i = 0; i < PAILLIER_BLUM_STATISTICAL_SECURITY; ++i) { + + uint8_t legendre_p; // 0 is QR, 1 if QNR + uint8_t legendre_q; + do { deterministic_rand(seed, n_len, y, &seed); } while (BN_cmp(y, priv->pub.n) >= 0); - uint8_t legendre_p; // 0 is QR, 1 if QNR - uint8_t legendre_q; + + //while we could do it only once, we still fill all z values for the backward compatibility if (!BN_mod_exp(proof.z[i], y, n_inverse_mod_phi_n, priv->pub.n, ctx)) + { goto cleanup; + } // Compute potential 4th root modulo prime, and get legendre symbol 0/1 using 4th power // This gives the 4th root of QR-corrected y (namely 8th root of y^2) if (!BN_mod(y_mod_pq, y, priv->p, ctx)) + { goto cleanup; + } if (!BN_mod_exp(p_4th_root, y_mod_pq, p_exp_4th, priv->p, ctx)) + { goto cleanup; + } if (!BN_mod_sqr(tmp, p_4th_root, priv->p, ctx)) + { goto cleanup; + } if (!BN_mod_sqr(tmp, tmp, priv->p, ctx)) + { goto cleanup; + } legendre_p = BN_cmp(tmp, y_mod_pq) != 0; if (!BN_mod(y_mod_pq, y, priv->q, ctx)) + { goto cleanup; + } if (!BN_mod_exp(q_4th_root, y_mod_pq, q_exp_4th, priv->q, ctx)) + { goto cleanup; + } if (!BN_mod_sqr(tmp, q_4th_root, priv->q, ctx)) + { goto cleanup; + } if (!BN_mod_sqr(tmp, tmp, priv->q, ctx)) + { goto cleanup; + } legendre_q = BN_cmp(tmp, y_mod_pq) != 0; // CRT compute x as 4th root of "QR-corrected" y (include w later) if (!BN_mod_mul(p_4th_root, p_4th_root, q_remainder, priv->pub.n, ctx)) + { goto cleanup; + } if (!BN_mod_mul(q_4th_root, q_4th_root, p_remainder, priv->pub.n, ctx)) + { goto cleanup; + } + + // We'll chose proof.x[i] randomly as +/- p_4th_root +/-q_4th_root + switch (get_2bit_number(random_bytes, i)) + { + case 0: + // p_4th_root + q_4th_root if (!BN_mod_add_quick(proof.x[i], p_4th_root, q_4th_root, priv->pub.n)) + { + goto cleanup; + } + break; + case 1: + // p_4th_root - q_4th_root + if (!BN_mod_sub_quick(proof.x[i], p_4th_root, q_4th_root, priv->pub.n)) + { goto cleanup; + } + break; + case 2: + // - p_4th_root + q_4th_root + if (!BN_mod_sub_quick(proof.x[i], q_4th_root, p_4th_root, priv->pub.n)) + { + goto cleanup; + } + break; + case 3: + // - p_4th_root - q_4th_root + if (!BN_mod_add_quick(proof.x[i], p_4th_root, q_4th_root, priv->pub.n) || + !BN_sub(proof.x[i], priv->pub.n, proof.x[i])) //negate x in mod n + { + goto cleanup; + } + break; + } // According to choice of w above with Jacobi symbol of (-1,1) proof.a[i] = legendre_q; @@ -847,44 +1276,75 @@ long paillier_generate_paillier_blum_zkp(const paillier_private_key_t *priv, con if (proof.b[i]) { if (!BN_mod_mul(proof.x[i], proof.x[i], correction, priv->pub.n, ctx)) + { goto cleanup; } } + } + serialize_paillier_blum_zkp(&proof, n_len, serialized_proof); ret = PAILLIER_SUCCESS; cleanup: if (ret < 0) + { ret = ERR_get_error() * -1; + } + + //where DUPed - need to be freed explicitly BN_clear_free(p_minus_1); BN_clear_free(q_minus_1); BN_clear_free(p_exp_4th); BN_clear_free(q_exp_4th); if (p_remainder) - BN_clear(p_remainder); + { + BN_clear(p_remainder); //will be freed with context + } if (q_remainder) - BN_clear(q_remainder); + { + BN_clear(q_remainder); //will be freed with context + } if (n_inverse_mod_phi_n) - BN_clear(n_inverse_mod_phi_n); + { + BN_clear(n_inverse_mod_phi_n); //will be freed with context + } if (a) - BN_clear(a); + { + BN_clear(a); //will be freed with context + } if (b) - BN_clear(b); + { + BN_clear(b); //will be freed with context + } if (correction) - BN_clear(correction); + { + BN_clear(correction); //will be freed with context + } if (tmp) - BN_clear(tmp); + { + BN_clear(tmp); //will be freed with context + } if (y_mod_pq) - BN_clear(y_mod_pq); + { + BN_clear(y_mod_pq); //will be freed with context + } if (p_4th_root) - BN_clear(p_4th_root); + { + BN_clear(p_4th_root); //will be freed with context + } if (q_4th_root) - BN_clear(q_4th_root); + { + BN_clear(q_4th_root); //will be freed with context + } if (y) - BN_clear(y); + { + BN_clear(y); //will be freed with context + } + BN_CTX_end(ctx); BN_CTX_free(ctx); + return ret; } @@ -901,20 +1361,47 @@ long paillier_verify_paillier_blum_zkp(const paillier_public_key_t *pub, const u long ret = -1; if (!pub) + { return PAILLIER_ERROR_INVALID_KEY; + } + if (!aad && aad_len) + { return PAILLIER_ERROR_INVALID_PARAM; + } + if (!serialized_proof || proof_len != paillier_blum_zkp_serialized_size(pub)) + { return PAILLIER_ERROR_INVALID_PARAM; + } + if (!BN_is_odd(pub->n)) // must be odd + { return PAILLIER_ERROR_INVALID_KEY; + } + if (BN_is_bit_set(pub->n, 1) != 0) // should be even because n % 4 == 1 + { return PAILLIER_ERROR_INVALID_KEY; + } + ctx = BN_CTX_new(); + if (!ctx) + { + return PAILLIER_ERROR_OUT_OF_MEMORY; + } + BN_CTX_start(ctx); + y = BN_CTX_get(ctx); + tmp = BN_CTX_get(ctx); + if (!y || !tmp) + { + goto cleanup; + } + if (BN_is_prime_ex(pub->n, 256, ctx, NULL)) { ret = PAILLIER_ERROR_INVALID_KEY; @@ -924,7 +1411,10 @@ long paillier_verify_paillier_blum_zkp(const paillier_public_key_t *pub, const u n_len = BN_num_bytes(pub->n); if (init_paillier_blum_zkp(&proof, ctx) != PAILLIER_SUCCESS) + { goto cleanup; + } + if (!deserialize_paillier_blum_zkp(&proof, n_len, serialized_proof)) { ret = PAILLIER_ERROR_INVALID_PROOF; @@ -934,27 +1424,42 @@ long paillier_verify_paillier_blum_zkp(const paillier_public_key_t *pub, const u SHA256_Init(&sha256_ctx); SHA256_Update(&sha256_ctx, PAILLIER_BLUM_ZKP_SALT, sizeof(PAILLIER_BLUM_ZKP_SALT)); if (aad) + { SHA256_Update(&sha256_ctx, aad, aad_len); + } + ret = update_with_bignum(&sha256_ctx, pub->n); if (ret != PAILLIER_SUCCESS) + { goto cleanup; + } + ret = update_with_bignum(&sha256_ctx, proof.w); if (ret != PAILLIER_SUCCESS) + { goto cleanup; + } + SHA256_Final(seed, &sha256_ctx); - ret = -1; - y = BN_CTX_get(ctx); - tmp = BN_CTX_get(ctx); - if (!y || !tmp) - goto cleanup; + ret = -1; //reset return value so goto cleanup could be used if (is_coprime_fast(proof.w, pub->n, ctx) != 1) { ret = PAILLIER_ERROR_INVALID_PROOF; goto cleanup; } - for (uint32_t i = 0; i < PAILLIER_BLUM_STATISTICAL_SECURITY; ++i) + + + //prepare tmp for the 1st iteration to verify z + if (!BN_mod_exp(tmp, proof.z[0], pub->n, pub->n, ctx)) + { + goto cleanup; + } + + // during development of 2 out of 2 MPC it was decided that + // PAILLIER_BLUM_STATISTICAL_SECURITY_MINIMAL_REQUIRED is enough + for (uint32_t i = 0; i < PAILLIER_BLUM_STATISTICAL_SECURITY_MINIMAL_REQUIRED; ++i) { do { @@ -966,38 +1471,75 @@ long paillier_verify_paillier_blum_zkp(const paillier_public_key_t *pub, const u ret = PAILLIER_ERROR_INVALID_PROOF; goto cleanup; } - if (!BN_mod_exp(tmp, proof.z[i], pub->n, pub->n, ctx)) + + // also z is enough to verify only once for the 1st y + if (0 == i) + { + if (BN_cmp(tmp, y) != 0) //ensure that y == z^n in mod n + { + ret = PAILLIER_ERROR_INVALID_PROOF; + goto cleanup; + } + + if (BN_is_one(y)) //theck that y is not 1 + { + ret = PAILLIER_ERROR_INVALID_PROOF; + goto cleanup; + } + + if (!BN_sub(tmp, pub->n, BN_value_one())) //tmp = n - 1 + { goto cleanup; - if (BN_cmp(tmp, y) != 0) + } + + if (0 == BN_cmp(tmp, y)) //check if y == n - 1 { ret = PAILLIER_ERROR_INVALID_PROOF; goto cleanup; } + } if (!BN_mod_sqr(tmp, proof.x[i], pub->n, ctx)) + { goto cleanup; + } + if (!BN_mod_sqr(tmp, tmp, pub->n, ctx)) + { goto cleanup; + } + if (proof.b[i]) { if (!BN_mod_mul(y, proof.w, y, pub->n, ctx)) + { goto cleanup; } + } + if (proof.a[i]) { if (!BN_sub(y, pub->n, y)) + { goto cleanup; } + } + if (BN_cmp(tmp, y) != 0) { ret = PAILLIER_ERROR_INVALID_PROOF; goto cleanup; } } + ret = PAILLIER_SUCCESS; + cleanup: if (ret < 0) + { ret = ERR_get_error() * -1; + } + BN_CTX_end(ctx); BN_CTX_free(ctx); diff --git a/src/common/crypto/shamir_secret_sharing/verifiable_secret_sharing.c b/src/common/crypto/shamir_secret_sharing/verifiable_secret_sharing.c index 26846b5..8c8fcd1 100644 --- a/src/common/crypto/shamir_secret_sharing/verifiable_secret_sharing.c +++ b/src/common/crypto/shamir_secret_sharing/verifiable_secret_sharing.c @@ -139,12 +139,12 @@ static verifiable_secret_sharing_status verifiable_secret_sharing_split_impl(con shares_local = (verifiable_secret_sharing_t*)calloc(1, sizeof(verifiable_secret_sharing_t)); if (!shares_local) return VERIFIABLE_SECRET_SHARING_OUT_OF_MEMORY; + BN_CTX_start(ctx); if (!(bn_secret = BN_bin2bn(secret, secret_len, NULL))) goto cleanup; if (!(bn_prime = BN_bin2bn(algebra->order(algebra), ELLIPTIC_CURVE_FIELD_SIZE, NULL))) goto cleanup; - BN_CTX_start(ctx); assert(BN_is_prime_ex(bn_prime, 1000, ctx, NULL)); BN_set_flags(bn_prime, BN_FLG_CONSTTIME); @@ -194,7 +194,9 @@ verifiable_secret_sharing_status verifiable_secret_sharing_split(const elliptic_ return VERIFIABLE_SECRET_SHARING_INVALID_PARAMETER; ctx = BN_CTX_new(); if (!ctx) - goto cleanup; + { + return VERIFIABLE_SECRET_SHARING_OUT_OF_MEMORY; + } BN_CTX_start(ctx); mat = (BIGNUM**)calloc(n * t, sizeof(BIGNUM*)); @@ -263,7 +265,9 @@ verifiable_secret_sharing_status verifiable_secret_sharing_split_with_custom_ids ctx = BN_CTX_new(); if (!ctx) - goto cleanup; + { + return VERIFIABLE_SECRET_SHARING_OUT_OF_MEMORY; + } BN_CTX_start(ctx); mat = (BIGNUM**)calloc(n * t, sizeof(BIGNUM*)); @@ -336,7 +340,7 @@ verifiable_secret_sharing_status verifiable_secret_sharing_get_share(const verif return VERIFIABLE_SECRET_SHARING_INVALID_PARAMETER; if (index >= shares->num_shares) return VERIFIABLE_SECRET_SHARING_INVALID_INDEX; - if (!shares->shares) + if (!shares->shares || !shares->shares[index]) return VERIFIABLE_SECRET_SHARING_UNKNOWN_ERROR; share->id = shares->ids[index]; memcpy(share->data, shares->shares[index], sizeof(shamir_secret_sharing_scalar_t)); @@ -478,15 +482,14 @@ verifiable_secret_sharing_status verifiable_secret_sharing_reconstruct(const ell } ctx = BN_CTX_new(); - if (!ctx) return ret; + BN_CTX_start(ctx); bn_prime = algebra->order_internal(algebra); if (!bn_prime) goto cleanup; - BN_CTX_start(ctx); sum = BN_CTX_get(ctx); if (!sum) goto cleanup; @@ -541,8 +544,8 @@ verifiable_secret_sharing_status verifiable_secret_sharing_verify_share(const el ctx = BN_CTX_new(); if (!ctx) return VERIFIABLE_SECRET_SHARING_OUT_OF_MEMORY; - BN_CTX_start(ctx); + x = BN_CTX_get(ctx); if (!x || !BN_set_word(x, id)) goto cleanup; diff --git a/src/common/crypto/zero_knowledge_proof/range_proofs.c b/src/common/crypto/zero_knowledge_proof/range_proofs.c index 0e9a36f..158419d 100644 --- a/src/common/crypto/zero_knowledge_proof/range_proofs.c +++ b/src/common/crypto/zero_knowledge_proof/range_proofs.c @@ -442,11 +442,13 @@ zero_knowledge_proof_status range_proof_exponent_zkpok_verify(const ring_pederse status = ZKP_VERIFICATION_FAILED; goto cleanup; } + if (!BN_bin2bn(proof->ciphertext, proof->ciphertext_len, tmp1)) { status = ZKP_OUT_OF_MEMORY; goto cleanup; } + if (is_coprime_fast(tmp1, paillier->n, ctx) != 1) { status = ZKP_VERIFICATION_FAILED; @@ -590,11 +592,13 @@ zero_knowledge_proof_status range_proof_exponent_zkpok_batch_verify(const ring_p status = ZKP_VERIFICATION_FAILED; goto cleanup; } + if (!BN_bin2bn(proofs[i].ciphertext, proofs[i].ciphertext_len, tmp1)) { status = ZKP_OUT_OF_MEMORY; goto cleanup; } + if (is_coprime_fast(tmp1, paillier->n, ctx) != 1) { status = ZKP_VERIFICATION_FAILED; @@ -999,6 +1003,7 @@ zero_knowledge_proof_status range_proof_diffie_hellman_zkpok_verify(const ring_p status = ZKP_VERIFICATION_FAILED; goto cleanup; } + if (!BN_set_bit(tmp1, (sizeof(elliptic_curve256_scalar_t) + ZKPOK_EPSILON_SIZE) * 8)) goto cleanup; if (BN_ucmp(zkpok.base.z1, tmp1) > 0) // range check @@ -1012,6 +1017,7 @@ zero_knowledge_proof_status range_proof_diffie_hellman_zkpok_verify(const ring_p status = ZKP_OUT_OF_MEMORY; goto cleanup; } + if (is_coprime_fast(tmp1, paillier->n, ctx) != 1) { status = ZKP_VERIFICATION_FAILED;