Progress on SEAL BFV implementation

This commit is contained in:
York Jasper Niebuhr 2025-11-28 12:21:20 +01:00
parent dff1d7460b
commit 0893f6044c
2 changed files with 105 additions and 24 deletions

View File

@ -58,7 +58,8 @@ public:
CTX_ARITHMETIC,
CTX_MEMORY,
CTX_INVALID_ARGUMENT,
CTX_COMPONENT
CTX_COMPONENT,
CTX_INTERNAL
};
private:
REASON m_reason;

View File

@ -5,6 +5,9 @@
#include <set>
#include <limits>
#include <unordered_map>
#include <sstream>
#include <string>
#include <cstring>
#include <seal/seal.h>
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::SEALContext> seal_bfv_ctx = std::make_unique<seal::SEALContext>(seal_bfv_params);
struct ct_buffer {
bool initialized = false;
seal::Ciphertext value;
};
struct seal_context_data {
int components;
seal::EncryptionParameters params;
std::unique_ptr<seal::SEALContext> ctx;
struct {
std::unique_ptr<seal::Decryptor> 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<int, ct_buffer> ciphertexts;
seal_context_data() : components{ 0 }, ct_id_factory{ 0 } {}
};
static minfree ctx_id_factory { 0 };
static std::unordered_map<int, seal_context_data> 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<seal::SEALContext>(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<seal::Decryptor>(*data.ctx, data.priv.private_key);
data.priv.decryptor = std::make_unique<seal::Decryptor>(*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<seal::BatchEncoder>(*data.ctx);
data.pub.encoder = std::make_unique<seal::BatchEncoder>(*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<seal::Encryptor>(*data.ctx, data.pub.public_key);
data.pub.encryptor = std::make_unique<seal::Encryptor>(*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<seal::Evaluator>(*data.ctx);
data.pub.evaluator = std::make_unique<seal::Evaluator>(*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<context>& 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<const char*>(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 {