172 lines
6.5 KiB
C++
172 lines
6.5 KiB
C++
#pragma once
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
#include <bit>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <array>
|
|
#include <memory>
|
|
|
|
namespace homcert::bfv {
|
|
|
|
constexpr std::size_t VECSIZE = 8192;
|
|
constexpr std::size_t PLAINMOD = 16760833; // 8192*2*1023+1 and prime
|
|
|
|
template<std::size_t N, typename = void>
|
|
struct smallest_uint;
|
|
|
|
template<std::size_t N>
|
|
struct smallest_uint<N, std::enable_if_t<(N <= UINT8_MAX)>> {
|
|
using type = std::uint8_t;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
struct smallest_uint<N, std::enable_if_t<(UINT8_MAX < N && N <= UINT16_MAX)>> {
|
|
using type = std::uint16_t;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
struct smallest_uint<N, std::enable_if_t<(UINT16_MAX < N && N <= UINT32_MAX)>> {
|
|
using type = std::uint32_t;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
struct smallest_uint<N, std::enable_if_t<(UINT32_MAX < N)>> {
|
|
using type = std::uint64_t;
|
|
};
|
|
|
|
struct plaintext {
|
|
using coefficient = typename smallest_uint<PLAINMOD>::type;
|
|
|
|
std::array<coefficient, VECSIZE> coefficients;
|
|
|
|
// Arithmetic...
|
|
};
|
|
|
|
using ciphertext = int;
|
|
|
|
// Exception for BFV errors in context
|
|
|
|
class bfv_exception : public std::exception {
|
|
public:
|
|
enum class REASON {
|
|
CTX_INVALID,
|
|
CTX_NOT_IMPLEMENTED,
|
|
CTX_NO_PRIVATE,
|
|
CTX_NO_PUBLIC,
|
|
CTX_ARITHMETIC,
|
|
CTX_MEMORY,
|
|
CTX_INVALID_ARGUMENT,
|
|
CTX_COMPONENT,
|
|
CTX_INTERNAL
|
|
};
|
|
private:
|
|
REASON m_reason;
|
|
std::string m_message;
|
|
public:
|
|
bfv_exception(REASON reason, std::string message);
|
|
REASON reason() const noexcept;
|
|
const char* what() const noexcept override;
|
|
};
|
|
|
|
// Inheriting from context class allows pluggability of implementations (e.g. SEAL vs. GPU)
|
|
|
|
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() = 0; // context default constructs empty
|
|
virtual void has_components(int& components) const = 0;
|
|
virtual void clone_components(int components, std::shared_ptr<context>& 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;
|
|
|
|
virtual void allocate(ciphertext& ct) = 0;
|
|
virtual void free(ciphertext ct) = 0;
|
|
virtual void serialize(ciphertext ct, void* buf, std::size_t& n) const = 0;
|
|
virtual void deserialize(ciphertext ct, const void* buf, std::size_t n) const = 0;
|
|
virtual void encrypt(const plaintext& pt, ciphertext ct) const = 0;
|
|
virtual void decrypt(ciphertext ct, plaintext& pt) const = 0; // private
|
|
virtual void add_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const = 0;
|
|
virtual void add_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const = 0;
|
|
virtual void sub_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const = 0;
|
|
virtual void sub_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const = 0;
|
|
virtual void mul_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const = 0;
|
|
virtual void mul_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const = 0;
|
|
virtual void rot_cipher_rows(ciphertext ct, int r, ciphertext res) const = 0;
|
|
virtual void swap_cipher_rows(ciphertext ct, ciphertext res) const = 0;
|
|
virtual void noise_budget(ciphertext ct, std::size_t& budget) const = 0; // private
|
|
};
|
|
|
|
/*
|
|
NOTE -> Implementations may defer operations or not wait for them to finish
|
|
For example, GPU implementations may dispatch multiplications when they arive
|
|
Additionally, a graph of running operations is maintained to handle data dependencies
|
|
Only when data is required to actually be present (e.g. decrypt), does the implementation wait
|
|
*/
|
|
|
|
// BFV implementation using Microsoft SEAL
|
|
|
|
struct seal_context : public context {
|
|
seal_context();
|
|
~seal_context();
|
|
|
|
void new_components() override;
|
|
void has_components(int& components) const override;
|
|
void clone_components(int components, std::shared_ptr<context>& ptr) const override;
|
|
void allocate(ciphertext& ct) override;
|
|
void free(ciphertext ct) override;
|
|
void serialize(ciphertext ct, void* buf, std::size_t& n) const override;
|
|
void deserialize(ciphertext ct, const void* buf, std::size_t n) const override;
|
|
void encrypt(const plaintext& pt, ciphertext ct) const override;
|
|
void decrypt(ciphertext ct, plaintext& pt) const override;
|
|
void add_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const override;
|
|
void add_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const override;
|
|
void sub_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const override;
|
|
void sub_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const override;
|
|
void mul_cipher_plain(ciphertext a, const plaintext& b, ciphertext res) const override;
|
|
void mul_cipher_cipher(ciphertext a, ciphertext b, ciphertext res) const override;
|
|
void rot_cipher_rows(ciphertext ct, int r, ciphertext res) const override;
|
|
void swap_cipher_rows(ciphertext ct, ciphertext res) const override;
|
|
void noise_budget(ciphertext ct, std::size_t& budget) const override;
|
|
private:
|
|
int m_id;
|
|
};
|
|
|
|
/*
|
|
activate_context(std::shared_ptr<bfv::context> ctx) -> thread local pointer is set
|
|
Raw ciphertext and plaintext classes always have the full 8192 coefficients (defined in context as static constexpr)
|
|
bfv::vector<...>
|
|
-> can be plaintext or ciphertext
|
|
-> can be base (owns plain-/ciphertext) or component (view to part of base)
|
|
-> can be local or remote
|
|
-> can be a single vector or multiple vectors/components (variadic)
|
|
-> arithmetic with component masks it out
|
|
-> arithmetic with base does operation on all components
|
|
-> tracks multiplicative depth
|
|
-> warning/error if multiplicative depth exceeds limit
|
|
-> use bootstrap member function to handle the warnings/errors
|
|
-> callbacks to reach peer in context
|
|
-> bootstrap_client (unchecked, just raw bootstrap, checks happen at an upper layer using other callbacks)
|
|
-> bootstrap_server_await (waits for client to make request)
|
|
-> bootstrap_server_serve (called immediately after request received with value to be returned)
|
|
-> automatically does secure reveal when cipher is transformed to plain
|
|
-> queues operations until used (cast to plaintext, communication with peer)
|
|
Programs are defined TWICE
|
|
-> local stuff is executed
|
|
-> remote stuff is hosted (e.g. bootstrapping server)
|
|
-> defined once from each side (differ e.g. in the plaintext inputs etc.)
|
|
-> program base class may be used to handle context setting
|
|
*/
|
|
|
|
}
|