From 0893f6044cfc98d1f3d11d31536fba2162f4a9b7 Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Fri, 28 Nov 2025 12:21:20 +0100 Subject: [PATCH] Progress on SEAL BFV implementation --- include/bfv.hpp | 3 +- src/seal_bfv.cpp | 126 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 105 insertions(+), 24 deletions(-) diff --git a/include/bfv.hpp b/include/bfv.hpp index 3d5352e..997445d 100644 --- a/include/bfv.hpp +++ b/include/bfv.hpp @@ -58,7 +58,8 @@ public: CTX_ARITHMETIC, CTX_MEMORY, CTX_INVALID_ARGUMENT, - CTX_COMPONENT + CTX_COMPONENT, + CTX_INTERNAL }; private: REASON m_reason; diff --git a/src/seal_bfv.cpp b/src/seal_bfv.cpp index d5d3c7d..1859791 100644 --- a/src/seal_bfv.cpp +++ b/src/seal_bfv.cpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include #include namespace homcert::bfv { @@ -38,12 +41,23 @@ public: } }; +static const seal::EncryptionParameters seal_bfv_params = []() { + seal::EncryptionParameters p { seal::scheme_type::bfv }; + p.set_poly_modulus_degree(VECSIZE); + p.set_coeff_modulus(seal::CoeffModulus::BFVDefault(VECSIZE)); + p.set_plain_modulus(PLAINMOD); + return p; +}(); +static std::unique_ptr seal_bfv_ctx = std::make_unique(seal_bfv_params); + +struct ct_buffer { + bool initialized = false; + seal::Ciphertext value; +}; + struct seal_context_data { int components; - seal::EncryptionParameters params; - std::unique_ptr ctx; - struct { std::unique_ptr decryptor; seal::SecretKey private_key; @@ -58,13 +72,20 @@ struct seal_context_data { seal::RelinKeys relin_keys; } pub; - seal_context_data() : components{ 0 } {} + minfree ct_id_factory; + std::unordered_map ciphertexts; + + seal_context_data() : components{ 0 }, ct_id_factory{ 0 } {} }; static minfree ctx_id_factory { 0 }; static std::unordered_map ctxs; seal_context::seal_context() : context{}, m_id{ -1 } { + if (!seal_bfv_ctx || !seal_bfv_ctx->parameters_set()) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID, + "Missing Microsoft SEAL parameter context!"); + int tmp_id; if (!ctx_id_factory.min(tmp_id)) return; @@ -99,21 +120,11 @@ void seal_context::new_components() { // Generate new private component - data.params = seal::EncryptionParameters{ seal::scheme_type::bfv }; - data.params.set_poly_modulus_degree(VECSIZE); - data.params.set_coeff_modulus(seal::CoeffModulus::BFVDefault(VECSIZE)); - data.params.set_plain_modulus(PLAINMOD); - - data.ctx = std::make_unique(data.params); - if (!data.ctx || !data.ctx->parameters_set()) - throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, - "Failed to allocate Microsoft SEAL context for new PRIVATE_COMPONENT!"); - - seal::KeyGenerator keygen { *data.ctx }; + seal::KeyGenerator keygen { *seal_bfv_ctx }; data.priv.private_key = keygen.secret_key(); - data.priv.decryptor = std::make_unique(*data.ctx, data.priv.private_key); + data.priv.decryptor = std::make_unique(*seal_bfv_ctx, data.priv.private_key); if (!data.priv.decryptor) throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, "Failed to create Microsoft SEAL decryptor for new PRIVATE_COMPONENT!"); @@ -126,17 +137,17 @@ void seal_context::new_components() { keygen.create_galois_keys(data.pub.galois_keys); keygen.create_relin_keys(data.pub.relin_keys); - data.pub.encoder = std::make_unique(*data.ctx); + data.pub.encoder = std::make_unique(*seal_bfv_ctx); if (!data.pub.encoder || data.pub.encoder->slot_count() != VECSIZE) throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, "Failed to create Microsoft SEAL encoder for new PUBLIC_COMPONENT!"); - data.pub.encryptor = std::make_unique(*data.ctx, data.pub.public_key); + data.pub.encryptor = std::make_unique(*seal_bfv_ctx, data.pub.public_key); if (!data.pub.encryptor) throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, "Failed to create Microsoft SEAL encryptor for new PUBLIC_COMPONENT!"); - data.pub.evaluator = std::make_unique(*data.ctx); + data.pub.evaluator = std::make_unique(*seal_bfv_ctx); if (!data.pub.evaluator) throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, "Failed to create Microsoft SEAL evaluator for new PUBLIC_COMPONENT"); @@ -163,12 +174,26 @@ void seal_context::clone_components(int components, std::shared_ptr& pt } void seal_context::allocate(ciphertext& ct) { + ct = -1; + if (!ctxs.contains(m_id)) throw bfv_exception(bfv_exception::REASON::CTX_INVALID, "Object does not hold a valid Microsoft SEAL context!"); seal_context_data& data = ctxs.at(m_id); - // TODO + + int tmp_id; + if (!data.ct_id_factory.min(tmp_id)) + throw bfv_exception(bfv_exception::REASON::CTX_MEMORY, + "Unable to allocate new ciphertext buffer!"); + + if (!data.ciphertexts.emplace(tmp_id, ct_buffer{}).second) { + data.ct_id_factory.free(tmp_id); + throw bfv_exception(bfv_exception::REASON::CTX_MEMORY, + "Unable to allocate new ciphertext buffer!"); + } + + ct = tmp_id; } void seal_context::free(ciphertext ct) { @@ -177,7 +202,13 @@ void seal_context::free(ciphertext ct) { "Object does not hold a valid Microsoft SEAL context!"); seal_context_data& data = ctxs.at(m_id); - // TODO + + if (!data.ciphertexts.contains(ct)) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID_ARGUMENT, + "The specified ciphertext does not exist in this context!"); + + data.ciphertexts.erase(ct); + data.ct_id_factory.free(ct); } void seal_context::serialize(ciphertext ct, void* buf, std::size_t& n) const { @@ -186,7 +217,37 @@ void seal_context::serialize(ciphertext ct, void* buf, std::size_t& n) const { "Object does not hold a valid Microsoft SEAL context!"); seal_context_data& data = ctxs.at(m_id); - // TODO + + if (!data.ciphertexts.contains(ct)) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID_ARGUMENT, + "The specified ciphertext does not exist in this context!"); + + const ct_buffer& ctbuf = data.ciphertexts.at(ct); + + if (!ctbuf.initialized) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID_ARGUMENT, + "The specified ciphertext is uninitialized!"); + + std::stringstream ss(std::ios::binary | std::ios::in | std::ios::out); + ctbuf.value.save(ss); + + if (!ss) + throw bfv_exception(bfv_exception::REASON::CTX_INTERNAL, + "Microsoft SEAL failed to serialize the specified ciphertext!"); + + std::string serialized = ss.str(); + + if (!buf) { + n = serialized.size(); + return; + } + + if (serialized.size() > n) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID_ARGUMENT, + "The given buffer is not sufficient to hold the serialized ciphertext!"); + + std::memcpy(buf, serialized.data(), serialized.size()); + n = serialized.size(); } void seal_context::deserialize(ciphertext ct, const void* buf, std::size_t n) const { @@ -195,7 +256,26 @@ void seal_context::deserialize(ciphertext ct, const void* buf, std::size_t n) co "Object does not hold a valid Microsoft SEAL context!"); seal_context_data& data = ctxs.at(m_id); - // TODO + + if (!buf || n == 0) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID_ARGUMENT, + "No serialized data was given!"); + + if (!data.ciphertexts.contains(ct)) + throw bfv_exception(bfv_exception::REASON::CTX_INVALID_ARGUMENT, + "The specified ciphertext does not exist in this context!"); + + ct_buffer& ctbuf = data.ciphertexts.at(ct); + + std::string sdata(reinterpret_cast(buf), n); + std::istringstream iss(std::move(sdata), std::ios::binary); + + try { + ctbuf.value.load(*seal_bfv_ctx, iss); + } catch (const std::exception& e) { + throw bfv_exception(bfv_exception::REASON::CTX_INTERNAL, + "Microsoft SEAL failed to deserialize the specified data!"); + } } void seal_context::encrypt(const plaintext& pt, ciphertext ct) const {