From dff1d7460b27652ac63beca56312b566358ace2e Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Thu, 27 Nov 2025 22:05:15 +0100 Subject: [PATCH] Beginnings of SEAL BFV implementation --- include/bfv.hpp | 15 +++- src/seal_bfv.cpp | 223 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 229 insertions(+), 9 deletions(-) diff --git a/include/bfv.hpp b/include/bfv.hpp index b407c37..3d5352e 100644 --- a/include/bfv.hpp +++ b/include/bfv.hpp @@ -51,6 +51,7 @@ using ciphertext = int; class bfv_exception : public std::exception { public: enum class REASON { + CTX_INVALID, CTX_NOT_IMPLEMENTED, CTX_NO_PRIVATE, CTX_NO_PUBLIC, @@ -74,10 +75,16 @@ struct context { static constexpr int PUBLIC_COMPONENT = 1; static constexpr int PRIVATE_COMPONENT = 2; + context(const context& other) = delete; + context(context&& other) = delete; + context& operator=(const context& other) = delete; + context& operator=(context&& other) = delete; + + context() = default; virtual ~context() = default; - virtual void new_components(int components) = 0; // context default constructs empty - virtual void has_components(int components) const = 0; + virtual void new_components() = 0; // context default constructs empty + virtual void has_components(int& components) const = 0; virtual void clone_components(int components, std::shared_ptr& ptr) const = 0; // virtual void dump_components(int components, void* buf, std::size_t& n) const = 0; // virtual void load_components(int components, const void* buf, std::size_t n) = 0; @@ -112,8 +119,8 @@ struct seal_context : public context { seal_context(); ~seal_context(); - void new_components(int components) override; - void has_components(int components) const override; + void new_components() override; + void has_components(int& components) const override; void clone_components(int components, std::shared_ptr& ptr) const override; void allocate(ciphertext& ct) override; void free(ciphertext ct) override; diff --git a/src/seal_bfv.cpp b/src/seal_bfv.cpp index 8290db7..d5d3c7d 100644 --- a/src/seal_bfv.cpp +++ b/src/seal_bfv.cpp @@ -2,86 +2,299 @@ #ifdef HOMCERT_BFV_SEAL +#include +#include +#include +#include + namespace homcert::bfv { -seal_context::seal_context() { +class minfree { + int next; + std::set freed; +public: + minfree(int start) : next{ start } {} + bool min(int& v) { + auto it = freed.begin(); + if (it == freed.end()) { + if (next >= std::numeric_limits::max()) + return false; + + v = next++; + return true; + } + + v = *it; + freed.erase(it); + return true; + } + + void free(int v) { + if (v >= next || freed.contains(v)) + return; // Just to be safe + + freed.insert(v); + } +}; + +struct seal_context_data { + int components; + + seal::EncryptionParameters params; + std::unique_ptr ctx; + + struct { + std::unique_ptr decryptor; + seal::SecretKey private_key; + } priv; + + struct { + std::unique_ptr encoder; + std::unique_ptr encryptor; + std::unique_ptr evaluator; + seal::PublicKey public_key; + seal::GaloisKeys galois_keys; + seal::RelinKeys relin_keys; + } pub; + + seal_context_data() : components{ 0 } {} +}; + +static minfree ctx_id_factory { 0 }; +static std::unordered_map ctxs; + +seal_context::seal_context() : context{}, m_id{ -1 } { + int tmp_id; + if (!ctx_id_factory.min(tmp_id)) + return; + + if (!ctxs.emplace(tmp_id, seal_context_data{}).second) { + ctx_id_factory.free(tmp_id); + return; + } + + m_id = tmp_id; } seal_context::~seal_context() { + if (m_id == -1) + return; + ctxs.erase(m_id); + ctx_id_factory.free(m_id); + m_id = -1; } -void seal_context::new_components(int components) { +void seal_context::new_components() { + 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); + + if (data.components & (PUBLIC_COMPONENT | PRIVATE_COMPONENT)) + throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, + "Object already holds valid 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 }; + + data.priv.private_key = keygen.secret_key(); + + data.priv.decryptor = std::make_unique(*data.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!"); + + data.components |= PRIVATE_COMPONENT; + + // Generate new private component + + keygen.create_public_key(data.pub.public_key); + keygen.create_galois_keys(data.pub.galois_keys); + keygen.create_relin_keys(data.pub.relin_keys); + + data.pub.encoder = std::make_unique(*data.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); + 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); + if (!data.pub.evaluator) + throw bfv_exception(bfv_exception::REASON::CTX_COMPONENT, + "Failed to create Microsoft SEAL evaluator for new PUBLIC_COMPONENT"); + + data.components |= PUBLIC_COMPONENT; } -void seal_context::has_components(int components) const { +void seal_context::has_components(int& components) const { + 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); + components = data.components; } void seal_context::clone_components(int components, std::shared_ptr& ptr) const { + 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 } void seal_context::allocate(ciphertext& ct) { + 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 } void seal_context::free(ciphertext ct) { + 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 } void seal_context::serialize(ciphertext ct, void* buf, std::size_t& n) const { + 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 } void seal_context::deserialize(ciphertext ct, const void* buf, std::size_t n) const { + 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 } void seal_context::encrypt(const plaintext& pt, ciphertext ct) const { + 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 } void seal_context::decrypt(ciphertext ct, plaintext& pt) const { + 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 } void seal_context::add_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const { + 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 } void seal_context::add_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const { + 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 } void seal_context::sub_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const { + 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 } void seal_context::sub_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const { + 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 } void seal_context::mul_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const { + 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 } void seal_context::mul_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const { + 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 } void seal_context::rot_cipher_rows(ciphertext ct, int r, ciphertext res) const { + 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 } void seal_context::swap_cipher_rows(ciphertext ct, ciphertext res) const { + 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 } void seal_context::noise_budget(ciphertext ct, std::size_t& budget) const { + 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 } } @@ -94,11 +307,11 @@ seal_context::seal_context() {} seal_context::~seal_context() {} -void seal_context::new_components(int components) { +void seal_context::new_components() { throw bfv_exception(bfv_exception::REASON::CTX_NOT_IMPLEMENTED, "BFV context for Microsoft SEAL is not implemented"); } -void seal_context::has_components(int components) const { +void seal_context::has_components(int& components) const { throw bfv_exception(bfv_exception::REASON::CTX_NOT_IMPLEMENTED, "BFV context for Microsoft SEAL is not implemented"); }