diff --git a/.bazelrc b/.bazelrc index 2c20285..adcc411 100644 --- a/.bazelrc +++ b/.bazelrc @@ -8,6 +8,7 @@ build --cxxopt=-Wextra build --cxxopt=-Werror # Suppress GCC false positives in Abseil (known issue with InlinedVector) +build --cxxopt=-Wno-unknown-warning-option build --cxxopt=-Wno-error=maybe-uninitialized # No exceptions needed (pure polynomial algebra, no OpenFHE) @@ -18,7 +19,7 @@ build --cxxopt=-O2 build --cxxopt=-g # Fast math for polynomial operations -build --cxxopt=-ffast-math +# build --cxxopt=-ffast-math # Test configuration test --test_output=errors diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..f46ccef --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,65 @@ +# f2chat AI Coding Instructions + +## Project Overview + +f2chat is a metadata-private messaging system using **Fully Homomorphic Encryption (FHE)**. +The core innovation is **Blind Routing**: the server routes encrypted messages using **Sheaf-Wreath Attention** without ever decrypting them. + +## Architecture + +- **Client**: Holds private keys. Encrypts polynomials (IDs, messages) and decrypts results. +- **Server**: Performs **blind algebraic routing** on encrypted data. + - **Input**: Encrypted polynomials (`Enc(P_alice)`, `Enc(P_bob)`, `Enc(message)`). + - **Operation**: Homomorphic addition, subtraction, scalar multiplication, and rotation. + - **Output**: Encrypted routed message stored at an encrypted mailbox location. +- **Constraint**: All server-side operations must be **Depth-0** (no bootstrapping). + +## Key Components + +- **`lib/crypto/fhe_context.h`**: Manages OpenFHE BGV context. + - Use `FHEContext::Create()` to initialize. + - Supports `HomomorphicAdd`, `HomomorphicSubtract`, `HomomorphicRotate`, `HomomorphicMultiplyScalar`. +- **`lib/crypto/encrypted_polynomial.h`**: Wrapper for FHE ciphertexts. + - **Server-Safe**: `Add`, `Subtract`, `Rotate`, `MultiplyScalar`, `ProjectToCharacter`. + - **Client-Only**: `Encrypt`, `Decrypt`. +- **`lib/crypto/polynomial.h`**: Plaintext polynomial arithmetic (Ring $Z_p[x]/(x^n+1)$). + +## Development Workflow + +- **Build System**: Bazel + - Build all: `bazel build //lib/...` + - Run tests: `bazel test //test/...` + - Run specific test: `bazel test //test/crypto:encrypted_polynomial_test --test_output=all` +- **Dependencies**: OpenFHE (via `third_party/openfhe.BUILD`), Abseil, Eigen, GoogleTest. + +## Local Setup & Running + +- **Prerequisites**: + - Bazel (latest version) + - C++ Compiler (Clang/GCC with C++17 support) + - Git +- **Setup**: + 1. Clone the repository. + 2. Run `bazel build //lib/...` to fetch dependencies (including OpenFHE) and build the core library. +- **Running Tests**: + - Run all tests: `bazel test //test/...` + - Run Aliceβ†’Bob integration test: `bazel test //test/integration:alice_to_bob_test --test_output=all` + - Run FHE stub tests: `bazel test //test/crypto:encrypted_polynomial_test --test_output=all` + +## Coding Conventions + +- **Language**: C++ (modern standards). +- **Style**: Google C++ Style (use `absl::StatusOr` for error handling). +- **FHE Patterns**: + - **NEVER** decrypt on the server. + - **ALWAYS** check for Depth-0 compatibility (avoid complex multiplications). + - Use `EncryptedPolynomial` for all high-level FHE logic. + - Use `FHEContext` for low-level OpenFHE interactions. + +## Current Status (Phase 2) + +- **Focus**: Implementing FHE infrastructure. +- **Active Tasks**: + - Implement OpenFHE integration in `lib/crypto/fhe_context.cc`. + - Implement `ProjectToCharacter` in `lib/crypto/encrypted_polynomial.cc`. + - Create `lib/network/encrypted_mailbox.{h,cc}`. diff --git a/BUILD.bazel b/BUILD.bazel index f09c17d..0a45b85 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1 +1,13 @@ # f2chat root BUILD file + +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_compile_commands", + targets = [ + "//lib/...", + "//test/...", + ], + exclude_headers = "external", + exclude_external_sources = True, +) diff --git a/MODULE.bazel b/MODULE.bazel index b93e901..ec28aa6 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,7 +4,10 @@ module(name = "f2chat", version = "2.0.0") # Core dependencies bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "com_google_absl") bazel_dep(name = "googletest", version = "1.15.2") +bazel_dep(name = "google_benchmark", version = "1.8.3") bazel_dep(name = "eigen", version = "3.4.0") +bazel_dep(name = "rules_foreign_cc", version = "0.12.0") +bazel_dep(name = "rules_cc", version = "0.0.2") # OpenFHE for homomorphic encryption # Note: OpenFHE is added via git_repository since it's not in BCR @@ -17,3 +20,24 @@ git_repository( tag = "v1.2.1", # Last stable version with Bazel support build_file = "//third_party:openfhe.BUILD", ) + +# Cereal for serialization (required by OpenFHE) +http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "cereal", + urls = ["https://github.com/USCiLab/cereal/archive/refs/tags/v1.3.2.tar.gz"], + sha256 = "16a7ad9b31ba5880dac55d62b5d6f243c3ebc8d46a3514149e56b5e7ea81f85f", + strip_prefix = "cereal-1.3.2", + build_file = "//third_party:cereal.BUILD", +) + +# Hedron's Compile Commands Extractor for Bazel +# https://github.com/hedronvision/bazel-compile-commands-extractor +bazel_dep(name = "hedron_compile_commands", dev_dependency = True) + +git_override( + module_name = "hedron_compile_commands", + remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", + commit = "abb61a688167623088f8768cc9264798df6a9d10", +) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index a4614d7..82979f2 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -43,6 +43,8 @@ "https://bcr.bazel.build/modules/eigen/3.4.0/MODULE.bazel": "c0929a2602e407d83edb5dbe7bfc90e0e65f11ce9030466858cb670b89599241", "https://bcr.bazel.build/modules/eigen/3.4.0/source.json": "e9ed671b71d257d306185a65a0e806484b8fbc6abf56ac091450d616f996e8ab", "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/google_benchmark/1.8.3/MODULE.bazel": "2349ac3adb7917fdc4378e85fae533015dae3cb583ad1bd5d2c2185106c7c403", + "https://bcr.bazel.build/modules/google_benchmark/1.8.3/source.json": "a97edcca10b7a487a8cc7c2f8843a68e512fa7f84f0d72a0f25d0dc877f568c9", "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", @@ -51,6 +53,7 @@ "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/libpfm/4.11.0/source.json": "caaffb3ac2b59b8aac456917a4ecf3167d40478ee79f15ab7a877ec9273937c9", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", @@ -87,6 +90,8 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.12.0/MODULE.bazel": "d850fab025ce79a845077035861034393f1cc1efc1d9d58d766272a26ba67def", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.12.0/source.json": "c97ddc022179fe30d1a9b94425d1e56d0a633f72332c55463e584a52ce1b38ac", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", @@ -150,6 +155,372 @@ }, "selectedYankedVersions": {}, "moduleExtensions": { + "@@rules_foreign_cc+//foreign_cc:extensions.bzl%tools": { + "general": { + "bzlTransitiveDigest": "oyRIiprjgbWcqn0+L4l3HJG9DfP18Sq5/i3MMWL/aUQ=", + "usagesDigest": "ohd0GUrp9Q8gXE8HHEHWJVYvVAX2Tnz70G0Dii4MKws=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_foreign_cc_framework_toolchain_linux": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:linux" + ] + } + }, + "rules_foreign_cc_framework_toolchain_freebsd": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:freebsd" + ] + } + }, + "rules_foreign_cc_framework_toolchain_windows": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:windows" + ] + } + }, + "rules_foreign_cc_framework_toolchain_macos": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:macos" + ] + } + }, + "rules_foreign_cc_framework_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository_hub", + "attributes": {} + }, + "cmake_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa", + "strip_prefix": "cmake-3.23.2", + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz" + ], + "patches": [ + "@@rules_foreign_cc+//toolchains:cmake-c++11.patch" + ] + } + }, + "gnumake_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3", + "strip_prefix": "make-4.4.1", + "urls": [ + "https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz", + "http://ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz" + ] + } + }, + "ninja_build_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "integrity": "sha256-ghvf9Io/aDvEuztvC1/nstZHz2XVKutjMoyRpsbfKFo=", + "strip_prefix": "ninja-1.12.1", + "urls": [ + "https://mirror.bazel.build/github.com/ninja-build/ninja/archive/v1.12.1.tar.gz", + "https://github.com/ninja-build/ninja/archive/v1.12.1.tar.gz" + ] + } + }, + "meson_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "567e533adf255de73a2de35049b99923caf872a455af9ce03e01077e0d384bed", + "strip_prefix": "meson-1.5.1", + "urls": [ + "https://mirror.bazel.build/github.com/mesonbuild/meson/releases/download/1.5.1/meson-1.5.1.tar.gz", + "https://github.com/mesonbuild/meson/releases/download/1.5.1/meson-1.5.1.tar.gz" + ] + } + }, + "glib_dev": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369", + "urls": [ + "https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip", + "https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip" + ] + } + }, + "glib_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e", + "strip_prefix": "glib-2.26.1", + "urls": [ + "https://mirror.bazel.build/download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz", + "https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz" + ] + } + }, + "glib_runtime": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973", + "urls": [ + "https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip", + "https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip" + ] + } + }, + "gettext_runtime": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079", + "urls": [ + "https://mirror.bazel.build/download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip", + "https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip" + ] + } + }, + "pkgconfig_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591", + "strip_prefix": "pkg-config-0.29.2", + "patches": [ + "@@rules_foreign_cc+//toolchains:pkgconfig-detectenv.patch", + "@@rules_foreign_cc+//toolchains:pkgconfig-makefile-vc.patch", + "@@rules_foreign_cc+//toolchains:pkgconfig-builtin-glib-int-conversion.patch" + ], + "urls": [ + "https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz", + "https://mirror.bazel.build/pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz" + ] + } + }, + "bazel_features": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "ba1282c1aa1d1fffdcf994ab32131d7c7551a9bc960fbf05f42d55a1b930cbfb", + "strip_prefix": "bazel_features-1.15.0", + "url": "https://github.com/bazel-contrib/bazel_features/releases/download/v1.15.0/bazel_features-v1.15.0.tar.gz" + } + }, + "bazel_skylib": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz" + ], + "sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728" + } + }, + "rules_python": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841", + "strip_prefix": "rules_python-0.23.1", + "url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz" + } + }, + "cmake-3.23.2-linux-aarch64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz" + ], + "sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e", + "strip_prefix": "cmake-3.23.2-linux-aarch64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" + } + }, + "cmake-3.23.2-linux-x86_64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz" + ], + "sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708", + "strip_prefix": "cmake-3.23.2-linux-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" + } + }, + "cmake-3.23.2-macos-universal": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz" + ], + "sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88", + "strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" + } + }, + "cmake-3.23.2-windows-i386": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip" + ], + "sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07", + "strip_prefix": "cmake-3.23.2-windows-i386", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" + } + }, + "cmake-3.23.2-windows-x86_64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip" + ], + "sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0", + "strip_prefix": "cmake-3.23.2-windows-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" + } + }, + "cmake_3.23.2_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//toolchains:prebuilt_toolchains_repository.bzl%prebuilt_toolchains_repository", + "attributes": { + "repos": { + "cmake-3.23.2-linux-aarch64": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "cmake-3.23.2-linux-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "cmake-3.23.2-macos-universal": [ + "@platforms//os:macos" + ], + "cmake-3.23.2-windows-i386": [ + "@platforms//cpu:x86_32", + "@platforms//os:windows" + ], + "cmake-3.23.2-windows-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "cmake" + } + }, + "ninja_1.12.1_linux": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip" + ], + "sha256": "6f98805688d19672bd699fbbfa2c2cf0fc054ac3df1f0e6a47664d963d530255", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.12.1_linux-aarch64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux-aarch64.zip" + ], + "sha256": "5c25c6570b0155e95fce5918cb95f1ad9870df5768653afe128db822301a05a1", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.12.1_mac": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-mac.zip" + ], + "sha256": "89a287444b5b3e98f88a945afa50ce937b8ffd1dcc59c555ad9b1baf855298c9", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.12.1_mac_aarch64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-mac.zip" + ], + "sha256": "89a287444b5b3e98f88a945afa50ce937b8ffd1dcc59c555ad9b1baf855298c9", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.12.1_win": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip" + ], + "sha256": "f550fec705b6d6ff58f2db3c374c2277a37691678d6aba463adcbb129108467a", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.12.1_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//toolchains:prebuilt_toolchains_repository.bzl%prebuilt_toolchains_repository", + "attributes": { + "repos": { + "ninja_1.12.1_linux": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "ninja_1.12.1_linux-aarch64": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "ninja_1.12.1_mac": [ + "@platforms//cpu:x86_64", + "@platforms//os:macos" + ], + "ninja_1.12.1_mac_aarch64": [ + "@platforms//cpu:aarch64", + "@platforms//os:macos" + ], + "ninja_1.12.1_win": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "ninja" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_foreign_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_foreign_cc+", + "rules_foreign_cc", + "rules_foreign_cc+" + ] + ] + } + }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { "bzlTransitiveDigest": "OlvsB0HsvxbR8ZN+J9Vf00X/+WVz/Y/5Xrq2LgcVfdo=", diff --git a/README.md b/README.md index 5f0ef91..34c688b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ Build a **truly metadata-private messaging system** where the server performs bl ## πŸ“Š Development Status ### βœ… Phase 1: Plaintext Polynomial Routing (COMPLETE) + **Status**: 32 tests passing, 1,340 lines of code + - βœ… Polynomial ring operations (Z_p[x]/(x^n + 1)) - βœ… Polynomial identities (device-held, unlinkable) - βœ… Algebraic routing (polynomial encoding/decoding) @@ -38,9 +40,11 @@ Build a **truly metadata-private messaging system** where the server performs bl **Limitation**: Server sees plaintext polynomial IDs - not true FHE! ### 🚧 Phase 2: FHE Infrastructure (IN PROGRESS) + **Status**: 9 FHE tests passing (stubs), infrastructure in place #### βœ… Completed (2025-11-11): + - βœ… OpenFHE dependency added to MODULE.bazel - βœ… FHEContext wrapper (`lib/crypto/fhe_context.{h,cc}`) - βœ… EncryptedPolynomial class (`lib/crypto/encrypted_polynomial.{h,cc}`) @@ -48,7 +52,8 @@ Build a **truly metadata-private messaging system** where the server performs bl - βœ… Test structure for FHE operations - βœ… Build system configured -#### πŸ”¨ TODO - OpenFHE Integration: +#### βœ… OpenFHE Integration (2025-12-14): + ```cpp // lib/crypto/fhe_context.cc - Lines 35-62 // Current: UnimplementedError stubs @@ -68,12 +73,13 @@ absl::StatusOr FHEContext::Create() { } ``` -**Files to implement**: -1. `lib/crypto/fhe_context.cc` - Fill in OpenFHE calls -2. `lib/crypto/encrypted_polynomial.cc:ProjectToCharacter()` - Homomorphic DFT -3. Update `third_party/openfhe.BUILD` for actual OpenFHE build +- βœ… `lib/crypto/fhe_context.cc` - Fill in OpenFHE calls +- βœ… `lib/crypto/encrypted_polynomial.cc:ProjectToCharacter()` - Homomorphic DFT +- βœ… Update `third_party/openfhe.BUILD` for actual OpenFHE build +- βœ… Benchmarking for FHE Operations (TODO: add stable benchmark results) ### πŸ“‹ Phase 3: Encrypted Mailbox Addressing (TODO) + **Goal**: Server stores messages at encrypted mailbox locations ```cpp @@ -91,12 +97,14 @@ class EncryptedMailbox { ``` **Tasks**: + - [ ] Create `lib/network/encrypted_mailbox.{h,cc}` - [ ] Implement homomorphic mailbox ID computation - [ ] Update server to use encrypted storage - [ ] Test: Server cannot determine which mailbox ### πŸ“‹ Phase 4: Homomorphic Routing (TODO) + **Goal**: Apply wreath-sheaf routing on encrypted polynomials ```cpp @@ -114,12 +122,14 @@ class RoutingPolynomial { ``` **Tasks**: + - [ ] Implement `HomomorphicEncodeRoute()` - [ ] Update `lib/network/patch.{h,cc}` for encrypted character projections - [ ] Update `lib/network/sheaf_router.{h,cc}` for encrypted routing - [ ] Test: Decrypt(ServerRoute(Enc(msg))) == msg ### πŸ“‹ Phase 5: Private Information Retrieval (TODO) + **Goal**: Bob retrieves messages without revealing his mailbox ```cpp @@ -141,17 +151,20 @@ class PIRServer { ``` **Options**: + 1. Integrate SealPIR (Microsoft Research, BFV-based) 2. Use SimplePIR (lattice-based, might be lighter) 3. Implement custom PIR using OpenFHE primitives **Tasks**: + - [ ] Research: SealPIR vs SimplePIR vs custom - [ ] Add PIR dependency to MODULE.bazel - [ ] Implement PIR client/server - [ ] Test: Server learns nothing about query ### πŸ“‹ Phase 6: End-to-End Integration (TODO) + **Goal**: Full Alice β†’ Bob flow with zero server knowledge ```cpp @@ -169,6 +182,7 @@ TEST(AliceToBobFHETest, TrueBlindRouting) { ``` **Success Criteria**: + - βœ… Server never calls Decrypt() - βœ… Server never sees plaintext polynomial IDs - βœ… Server never sees plaintext mailbox IDs @@ -180,6 +194,7 @@ TEST(AliceToBobFHETest, TrueBlindRouting) { ## πŸ—οΈ Architecture ### Current (Phase 1 - Plaintext Routing) + ``` Alice Device: β”œβ”€ Real ID: "alice@example.com" (never sent) @@ -197,6 +212,7 @@ Bob Device: ``` ### Target (Phase 2+ - True FHE Routing) + ``` Alice Device: β”œβ”€ Real ID: "alice@example.com" (never sent) @@ -225,6 +241,7 @@ Bob Device: ## πŸ“ File Structure ### Existing Files (Phase 1) + ``` lib/crypto/ β”œβ”€β”€ polynomial.{h,cc} # Ring operations (Z_p[x]/(x^n+1)) @@ -248,6 +265,7 @@ test/ ``` ### New Files (Phase 2+) + ``` lib/crypto/ β”œβ”€β”€ fhe_context.{h,cc} # βœ… CREATED - OpenFHE wrapper (stubs) @@ -271,6 +289,7 @@ third_party/ ## πŸš€ Quick Start (Current State) ### Build & Test (Phase 1 - Plaintext) + ```bash # Build all libraries bazel build //lib/... @@ -283,6 +302,7 @@ bazel test //test/integration:alice_to_bob_test --test_output=all ``` ### Test FHE Infrastructure (Phase 2 - Stubs) + ```bash # Test encrypted polynomial (9 tests - all return UnimplementedError) bazel test //test/crypto:encrypted_polynomial_test --test_output=all @@ -295,23 +315,27 @@ bazel test //test/crypto:encrypted_polynomial_test --test_output=all ## πŸ”¬ Research Foundation This project implements: + > **"An Algebraic Theory of Learnability: Solving Diverse Problems with a Unified Sheaf-Wreath Attention"** > bon-cdp (shakilflynn@gmail.com), November 2025: https://github.com/bon-cdp/notes/blob/main/c.pdf ### Key Theoretical Components **Wreath Product** (Position-Dependent Routing): + - Network positions have character distributions (DFT basis) - Routing weights: `w[position][character]` - Learned via closed-form solve: `w* = (A^H A)^{-1} A^H b` (Theorem 2.1) **Sheaf** (Global Consistency): + - Network divided into patches (geographic regions) - Each patch has local routing algebra - Gluing constraints ensure message delivery - Zero cohomological obstruction = guaranteed delivery **FHE Application** (Novel Contribution): + - Server applies wreath-sheaf routing to **encrypted polynomials** - Position-dependent weights applied homomorphically - Character projections computed via homomorphic DFT @@ -322,12 +346,15 @@ This project implements: ## 🎯 Next Steps for Engineers ### πŸ”₯ IMMEDIATE (This Week): + 1. **Implement OpenFHE Integration** (`lib/crypto/fhe_context.cc`) + - Replace UnimplementedError stubs with OpenFHE BGV calls - File: Lines 35-180 - Estimated: 4-6 hours 2. **Implement Homomorphic Character Projection** (`lib/crypto/encrypted_polynomial.cc`) + - ProjectToCharacter() - Line 96 - Homomorphic DFT on encrypted polynomials - Estimated: 6-8 hours @@ -338,13 +365,16 @@ This project implements: - Estimated: 2 hours ### πŸ“… SHORT TERM (Next 2 Weeks): + 1. **Encrypted Mailbox Addressing** (Phase 3) + - Create `lib/network/encrypted_mailbox.{h,cc}` - Homomorphic mailbox ID computation - Server-side blind storage - Estimated: 3-4 days 2. **Homomorphic Routing** (Phase 4) + - Implement `HomomorphicEncodeRoute()` - Update patch/sheaf router for encrypted data - Estimated: 4-5 days @@ -355,6 +385,7 @@ This project implements: - Estimated: 5-7 days ### 🎯 MILESTONE (End of Month): + - βœ… Full Alice β†’ Bob FHE routing test passing - βœ… Server performs zero decryptions - βœ… Depth-0 operations verified @@ -365,16 +396,20 @@ This project implements: ## πŸ“š Resources for Engineers ### OpenFHE Documentation + - **Main docs**: https://openfhe-development.readthedocs.io/ - **BGV examples**: `openfhe-development/src/pke/examples/` - **API reference**: https://openfhe-development.readthedocs.io/en/latest/api.html ### Key Papers + 1. OpenFHE library paper: https://eprint.iacr.org/2022/915.pdf 2. SealPIR: https://github.com/microsoft/SealPIR 3. BGV scheme: https://eprint.iacr.org/2011/277.pdf +4. **[Prerequisite Reading List](docs/reading_list.md)**: Essential reading for new contributors. ### Our Theory Paper + - See: `docs/sheaf_wreath_theory.pdf` (LaTeX source included) - Key insight: Optimization replaced by algebra when problem has right symmetry @@ -383,11 +418,13 @@ This project implements: ## πŸ› Known Issues & Limitations ### Phase 1 (Plaintext): + - ❌ Server sees plaintext polynomial IDs (not true metadata privacy) - ❌ No actual encryption (just "unlinkable" pseudonyms) - βœ… But: Routing algebra is correct (ready for FHE!) ### Phase 2 (Current): + - ⚠️ OpenFHE integration incomplete (stubs return UnimplementedError) - ⚠️ Homomorphic character projection not implemented - ⚠️ No encrypted mailbox addressing yet @@ -412,6 +449,7 @@ Apache 2.0 - See LICENSE ## πŸ™ Acknowledgments This project builds on: + - OpenFHE team for the incredible FHE library - Microsoft Research for SealPIR - Sheaf theory (algebraic topology) diff --git a/docs/reading_list.md b/docs/reading_list.md new file mode 100644 index 0000000..00a5542 --- /dev/null +++ b/docs/reading_list.md @@ -0,0 +1,74 @@ +# f2chat Prerequisite Reading List + +This document outlines the essential knowledge required to understand and contribute to `f2chat`. It is structured for a Computer Science undergraduate with a strong interest in cryptography and algebraic topology. + +## 1. Cryptography Foundations + +Before diving into FHE, you need a solid grasp of modern cryptography and ring theory. + +- **Modular Arithmetic & Ring Theory**: + + - **Concept**: Understanding $\mathbb{Z}_q$ and polynomial rings $\mathbb{Z}_q[x]/(x^n+1)$. + - **Resource**: **A Book of Abstract Algebra (Charles C. Pinter)** + - **Chapter 10**: Cyclic Groups (Understanding $\mathbb{Z}_n$). + - **Chapter 17-19**: Rings, Ideals, and Quotient Rings (Crucial for modular arithmetic). + - **Chapter 24**: Rings of Polynomials (The structure of our ciphertexts). + - **Chapter 25**: Factoring Polynomials (Irreducibility and $x^n+1$). + - **Why**: FHE ciphertexts are polynomials in a specific ring structure. + +- **Lattice-Based Cryptography**: + + - **Concept**: Learning with Errors (LWE) and Ring-LWE problems. + - **Resource**: [A Decade of Lattice Cryptography (Peikert)](https://eprint.iacr.org/2015/939.pdf) - _Read sections 1 & 2 for high-level intuition._ + - **Why**: The security of BGV (the scheme we use) relies on the hardness of these problems. + +- **Fully Homomorphic Encryption (FHE)**: + - **Concept**: Computing on encrypted data without decryption. + - **Resource**: [FHE for the rest of us (Microsoft Research)](https://www.microsoft.com/en-us/research/blog/fully-homomorphic-encryption-for-the-rest-of-us/) + - **Deep Dive**: [The BGV Scheme (Brakerski-Gentry-Vaikuntanathan)](https://eprint.iacr.org/2011/277.pdf) - _Focus on the "Leveled FHE" concept._ + +## 2. The OpenFHE Library + +We use OpenFHE as our cryptographic backend. + +- **Getting Started**: + + - **Resource**: [OpenFHE Getting Started Guide](https://openfhe-development.readthedocs.io/en/latest/intro/installation.html) + - **Key Sections**: Installation, "Your First OpenFHE Program". + +- **BGV in OpenFHE**: + - **Resource**: [OpenFHE BGV Example](https://github.com/openfheorg/openfhe-development/blob/main/src/pke/examples/simple-integers-bgvrns.cpp) + - **Why**: This example mirrors how we initialize our `FHEContext` and perform basic operations. + +## 3. Mathematical Framework (Sheaf & Wreath) + +This is the unique theoretical core of `f2chat`. + +- **Sheaf Theory**: + + - **Concept**: Local data consistency (patches) leading to global solutions. + - **Resource**: [Sheaf Theory for Undergraduates (Gallier)](https://www.cis.upenn.edu/~jean/sheaves-cohomology.pdf) - _Read the introduction and first chapter._ + - **Intuition**: Think of a "sheaf" as a way to glue local routing rules together to form a valid global route. + +- **Wreath Products**: + - **Concept**: A group construction that captures "position-dependent" symmetry (like a rubik's cube or a network with local structure). + - **Resource**: [Wreath Product (Wikipedia)](https://en.wikipedia.org/wiki/Wreath_product) - _Focus on the "Group Action" definition._ + - **Why**: Our routing attention mechanism is a "Wreath-Sheaf" attention, meaning it respects these specific symmetries. + +## 4. Engineering & Tooling + +Practical skills needed to build and test the system. + +- **Bazel Build System**: + + - **Resource**: [Bazel Tutorial: C++](https://bazel.build/tutorials/cpp) + - **Why**: We use Bazel for hermetic builds and dependency management. + +- **Modern C++ (C++17/20)**: + + - **Resource**: [A Tour of C++ (Stroustrup)](https://www.stroustrup.com/tour.html) + - **Key Concepts**: `std::shared_ptr`, `std::vector`, Templates, `absl::StatusOr` (from Abseil). + +- **Google Test (GTest)**: + - **Resource**: [GoogleTest Primer](https://google.github.io/googletest/primer.html) + - **Why**: All our verification is done via unit and integration tests. diff --git a/lib/crypto/BUILD.bazel b/lib/crypto/BUILD.bazel index de1c06b..c9d42a8 100644 --- a/lib/crypto/BUILD.bazel +++ b/lib/crypto/BUILD.bazel @@ -1,3 +1,5 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + cc_library( name = "polynomial", hdrs = [ @@ -45,12 +47,13 @@ cc_library( name = "fhe_context", hdrs = ["fhe_context.h"], srcs = ["fhe_context.cc"], + copts = ["-fexceptions"], # OpenFHE requires exceptions deps = [ ":polynomial", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - # "@openfhe//:openfhe_pke", # Uncomment when OpenFHE build is working + "@openfhe//:openfhe_pke", ], visibility = ["//visibility:public"], ) @@ -59,6 +62,7 @@ cc_library( name = "encrypted_polynomial", hdrs = ["encrypted_polynomial.h"], srcs = ["encrypted_polynomial.cc"], + copts = ["-fexceptions"], # OpenFHE requires exceptions deps = [ ":polynomial", ":fhe_context", diff --git a/lib/crypto/encrypted_polynomial.cc b/lib/crypto/encrypted_polynomial.cc index 2d11327..41607b2 100644 --- a/lib/crypto/encrypted_polynomial.cc +++ b/lib/crypto/encrypted_polynomial.cc @@ -7,6 +7,59 @@ namespace f2chat { +namespace { +// Helper: Compute modular exponentiation (base^exp mod modulus) +int64_t ModPow(int64_t base, int64_t exp, int64_t modulus) { + int64_t result = 1; + base %= modulus; + while (exp > 0) { + if (exp & 1) { + result = (result * base) % modulus; + } + base = (base * base) % modulus; + exp >>= 1; + } + return result; +} + +// Helper: Compute modular inverse using extended Euclidean algorithm +int64_t ModInverse(int64_t a, int64_t modulus) { + int64_t m0 = modulus, t, q; + int64_t x0 = 0, x1 = 1; + + if (modulus == 1) return 0; + + while (a > 1) { + q = a / modulus; + t = modulus; + modulus = a % modulus; + a = t; + t = x0; + x0 = x1 - q * x0; + x1 = t; + } + + if (x1 < 0) x1 += m0; + return x1; +} + +// Helper: Find a primitive nth root of unity modulo p +// For p = 65537 and n dividing (p-1), we can find Ο‰ such that Ο‰^n ≑ 1 (mod p) +int64_t FindRootOfUnity(int n, int64_t modulus) { + // For modulus = 65537 = 2^16 + 1, we know p-1 = 2^16 + // So any power of 2 divides (p-1) + + // A generator for Z_p^* is typically a small number + // For p = 65537, g = 3 is a generator + int64_t g = 3; + + // Ο‰ = g^((p-1)/n) is a primitive nth root of unity + int64_t exponent = (modulus - 1) / n; + return ModPow(g, exponent, modulus); +} + +} // namespace + // Static factory: Encrypt plaintext polynomial absl::StatusOr EncryptedPolynomial::Encrypt( const Polynomial& polynomial, @@ -96,38 +149,55 @@ absl::StatusOr EncryptedPolynomial::Negate( absl::StatusOr EncryptedPolynomial::ProjectToCharacter( int character_index, const FHEContext& fhe_context) const { - (void)fhe_context; // Suppress unused parameter warning - if (character_index < 0 || character_index >= RingParams::kNumCharacters) { return absl::InvalidArgumentError(absl::StrFormat( "Invalid character index: %d (must be 0 to %d)", character_index, RingParams::kNumCharacters - 1)); } - // TODO: Implement homomorphic character projection - // - // Planned implementation: - // 1. Compute DFT basis weights for character Ο‡β±Ό - // 2. For each position k, apply homomorphic rotation + scalar multiplication: - // proj = Ξ£β‚– Ο‡β±Ό(k) * Rotate(Enc(poly), k) - // 3. Scale by 1/n (using homomorphic scalar multiplication) - // - // This allows server to compute character projections on encrypted data! - // - // Formula: - // Proj_Ο‡β±Ό(Enc(p)) = (1/n) Ξ£β‚– Ο‡β±Ό(k) * Enc(p(ωᡏ)) - // where Ο‰ is a primitive nth root of unity. - // - // Example (for character 0, identity): - // Enc(Proj_Ο‡β‚€(p)) = (1/n) * Enc(sum of all coefficients) - // - // This is depth-0 because: - // - Rotation: depth-0 (automorphism) - // - Scalar multiplication: depth-0 (plaintext-ciphertext) - // - Addition: depth-0 - - return absl::UnimplementedError( - "EncryptedPolynomial::ProjectToCharacter() - Homomorphic DFT pending"); + try { + const int n = RingParams::kNumCharacters; + const int64_t modulus = RingParams::kModulus; + + // Find primitive nth root of unity modulo p + int64_t omega = FindRootOfUnity(n, modulus); + + // Compute character projection using DFT formula: + // Proj_Ο‡β±Ό(p) = (1/n) * Ξ£β‚– Ο‡β±Ό(k) * p(ωᡏ) + // where Ο‡β±Ό(k) = Ο‰^(j*k) mod p + + // Start with the k=0 term (no rotation needed) + // Ο‡β±Ό(0) = Ο‰^0 = 1, so we just scale by 1 + auto result = *this; + + // For each position k > 0, compute Ο‰^(j*k) and add scaled rotated ciphertext + for (int k = 1; k < n; ++k) { + // Compute character value: Ο‡β±Ό(k) = Ο‰^(j*k) mod p + int64_t chi_jk = ModPow(omega, static_cast(character_index) * k, modulus); + + // Rotate ciphertext by k positions + auto rotated_or = Rotate(k, fhe_context); + if (!rotated_or.ok()) return rotated_or.status(); + + // Scale by character value + auto scaled_or = rotated_or.value().MultiplyScalar(chi_jk, fhe_context); + if (!scaled_or.ok()) return scaled_or.status(); + + // Add to accumulator + auto sum_or = result.Add(scaled_or.value(), fhe_context); + if (!sum_or.ok()) return sum_or.status(); + + result = sum_or.value(); + } + + // Scale by 1/n (modular inverse of n) + int64_t n_inv = ModInverse(n, modulus); + return result.MultiplyScalar(n_inv, fhe_context); + + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Character projection failed: %s", e.what())); + } } absl::StatusOr> diff --git a/lib/crypto/fhe_context.cc b/lib/crypto/fhe_context.cc index c6b0858..cbc71f1 100644 --- a/lib/crypto/fhe_context.cc +++ b/lib/crypto/fhe_context.cc @@ -5,101 +5,124 @@ #include "lib/crypto/fhe_context.h" #include "absl/strings/str_format.h" -// Note: OpenFHE headers will be included here once the build is working -// For now, we'll create stub implementations to get the structure in place - namespace f2chat { // Static factory method absl::StatusOr FHEContext::Create() { - // TODO: Initialize OpenFHE crypto context with BGV scheme - // - // Planned implementation: - // 1. Create CryptoContext with BGV scheme - // 2. Set parameters: - // - Ring dimension: RingParams::kDegree (64/256/4096) - // - Modulus: RingParams::kModulus (65537) - // - Security level: HEStd_128_classic - // - Multiplicative depth: 0 (depth-0 operations only!) - // 3. Enable features: - // - Encryption - // - SHE (for homomorphic operations) - // - Leveled SHE (for efficient depth-0 operations) - // - // Example OpenFHE code: - // CCParams parameters; - // parameters.SetMultiplicativeDepth(0); - // parameters.SetPlaintextModulus(RingParams::kModulus); - // parameters.SetRingDim(RingParams::kDegree); - // CryptoContext cc = GenCryptoContext(parameters); - // cc->Enable(PKE); - // cc->Enable(KEYSWITCH); - // cc->Enable(LEVELEDSHE); - - return absl::UnimplementedError( - "FHEContext::Create() - OpenFHE integration pending. " - "This will be implemented once OpenFHE build is configured."); + try { + // Create BGV parameters for depth-0 operations + lbcrypto::CCParams parameters; + + // Set multiplicative depth to 0 (depth-0 operations only!) + // This means no ciphertext-ciphertext multiplications are supported, + // but we can still do: Add, Sub, Scalar multiplication, Rotation + parameters.SetMultiplicativeDepth(0); + + // Set plaintext modulus to match our ring parameters + parameters.SetPlaintextModulus(RingParams::kModulus); + + // Set ring dimension - OpenFHE may adjust this based on security + // We request kDegree slots + parameters.SetBatchSize(RingParams::kDegree); + + // Set security level to 128-bit + parameters.SetSecurityLevel(lbcrypto::HEStd_128_classic); + + // Generate the crypto context + CryptoContext cc = lbcrypto::GenCryptoContext(parameters); + + // Enable required features + cc->Enable(lbcrypto::PKE); // Public-key encryption + cc->Enable(lbcrypto::KEYSWITCH); // Key switching (for rotation) + cc->Enable(lbcrypto::LEVELEDSHE); // Leveled SHE (for homomorphic ops) + + return FHEContext(cc); + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Failed to create FHE context: %s", e.what())); + } } absl::StatusOr FHEContext::GenerateKeyPair() const { - // TODO: Generate FHE key pair using OpenFHE - // - // Planned implementation: - // KeyPair kp = crypto_context_->KeyGen(); - // crypto_context_->EvalMultKeyGen(kp.secretKey); - // - // Generate rotation keys for all positions: - // std::vector rotations; - // for (int i = 1; i < RingParams::kDegree; ++i) { - // rotations.push_back(i); - // rotations.push_back(-i); - // } - // crypto_context_->EvalRotateKeyGen(kp.secretKey, rotations); - // - // return FHEKeyPair{kp.publicKey, kp.secretKey}; - - return absl::UnimplementedError( - "FHEContext::GenerateKeyPair() - OpenFHE integration pending."); + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + + try { + // Generate key pair + KeyPair kp = crypto_context_->KeyGen(); + + // Generate evaluation keys for multiplication (even though we're depth-0, + // we need this for scalar multiplication in some cases) + crypto_context_->EvalMultKeyGen(kp.secretKey); + + // Generate rotation keys for all positions we might need + // For homomorphic DFT, we need rotations by all positions + std::vector rotations; + for (int i = 1; i < RingParams::kDegree; ++i) { + rotations.push_back(i); + rotations.push_back(-i); + } + crypto_context_->EvalRotateKeyGen(kp.secretKey, rotations); + + return FHEKeyPair{kp.publicKey, kp.secretKey}; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Failed to generate key pair: %s", e.what())); + } } absl::StatusOr FHEContext::Encrypt( const std::vector& coefficients, const PublicKey& public_key) const { - (void)public_key; // Suppress unused parameter warning (stub implementation) - + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + if (coefficients.size() > static_cast(RingParams::kDegree)) { return absl::InvalidArgumentError(absl::StrFormat( "Too many coefficients: %d (max: %d)", coefficients.size(), RingParams::kDegree)); } - // TODO: Encrypt using OpenFHE - // - // Planned implementation: - // Plaintext pt = crypto_context_->MakePackedPlaintext(coefficients); - // Ciphertext ct = crypto_context_->Encrypt(public_key, pt); - // return ct; - - return absl::UnimplementedError( - "FHEContext::Encrypt() - OpenFHE integration pending."); + try { + // Create a packed plaintext from coefficients + Plaintext pt = crypto_context_->MakePackedPlaintext(coefficients); + + // Encrypt using the public key + Ciphertext ct = crypto_context_->Encrypt(public_key, pt); + + return ct; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Encryption failed: %s", e.what())); + } } absl::StatusOr> FHEContext::Decrypt( const Ciphertext& ciphertext, const PrivateKey& private_key) const { - (void)ciphertext; // Suppress unused parameter warning - (void)private_key; - - // TODO: Decrypt using OpenFHE - // - // Planned implementation: - // Plaintext pt; - // crypto_context_->Decrypt(private_key, ciphertext, &pt); - // std::vector result = pt->GetPackedValue(); - // return result; - - return absl::UnimplementedError( - "FHEContext::Decrypt() - OpenFHE integration pending."); + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + + try { + Plaintext pt; + crypto_context_->Decrypt(private_key, ciphertext, &pt); + + // Get the packed values - these are the polynomial coefficients + std::vector result = pt->GetPackedValue(); + + // Resize to match our expected degree + if (result.size() > static_cast(RingParams::kDegree)) { + result.resize(RingParams::kDegree); + } + + return result; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Decryption failed: %s", e.what())); + } } // Homomorphic operations @@ -107,74 +130,108 @@ absl::StatusOr> FHEContext::Decrypt( absl::StatusOr FHEContext::HomomorphicAdd( const Ciphertext& ct1, const Ciphertext& ct2) const { - (void)ct1; // Suppress unused parameter warning - (void)ct2; - - // TODO: Homomorphic addition using OpenFHE - // - // Planned implementation: - // Ciphertext result = crypto_context_->EvalAdd(ct1, ct2); - // return result; - // - // Note: This is depth-0 (no bootstrapping needed!) - - return absl::UnimplementedError( - "FHEContext::HomomorphicAdd() - OpenFHE integration pending."); + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + + try { + // Homomorphic addition - depth-0 operation! + Ciphertext result = crypto_context_->EvalAdd(ct1, ct2); + return result; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Homomorphic addition failed: %s", e.what())); + } } absl::StatusOr FHEContext::HomomorphicSubtract( const Ciphertext& ct1, const Ciphertext& ct2) const { - (void)ct1; // Suppress unused parameter warning - (void)ct2; - - // TODO: Homomorphic subtraction using OpenFHE - // - // Planned implementation: - // Ciphertext result = crypto_context_->EvalSub(ct1, ct2); - // return result; - // - // Note: This is depth-0 (no bootstrapping needed!) - - return absl::UnimplementedError( - "FHEContext::HomomorphicSubtract() - OpenFHE integration pending."); + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + + try { + // Homomorphic subtraction - depth-0 operation! + Ciphertext result = crypto_context_->EvalSub(ct1, ct2); + return result; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Homomorphic subtraction failed: %s", e.what())); + } } +// Since this is depth-0 BGV (no multiplicative depth available) +// True homomorphic multiplication (EvalMult) isn't possible without consuming depth +// For depth-0 BGV, we use repeated addition for scalar multiplication +// This is less efficient than EvalMult but works with depth-0 absl::StatusOr FHEContext::HomomorphicMultiplyScalar( const Ciphertext& ciphertext, int64_t scalar) const { - (void)ciphertext; // Suppress unused parameter warning - (void)scalar; - - // TODO: Homomorphic scalar multiplication using OpenFHE - // - // Planned implementation: - // Ciphertext result = crypto_context_->EvalMult(ciphertext, scalar); - // return result; - // - // Note: This is depth-0 (plaintext-ciphertext multiplication!) - - return absl::UnimplementedError( - "FHEContext::HomomorphicMultiplyScalar() - OpenFHE integration pending."); + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + + try { + + if (scalar == 0) { + // Return encryption of zeros + std::vector zeros(RingParams::kDegree, 0); + auto zero_pt = crypto_context_->MakePackedPlaintext(zeros); + return crypto_context_->EvalAdd(ciphertext, + crypto_context_->EvalNegate(ciphertext)); + } + + if (scalar == 1) { + return ciphertext; + } + + // Handle negative scalars + bool negate = scalar < 0; + int64_t abs_scalar = negate ? -scalar : scalar; + + // Use binary method for efficiency: O(log n) additions instead of O(n) + Ciphertext result = ciphertext; + Ciphertext accumulator = ciphertext; + abs_scalar--; // We already have one copy in result + + while (abs_scalar > 0) { + if (abs_scalar & 1) { + result = crypto_context_->EvalAdd(result, accumulator); + } + abs_scalar >>= 1; + if (abs_scalar > 0) { + accumulator = crypto_context_->EvalAdd(accumulator, accumulator); + } + } + + if (negate) { + result = crypto_context_->EvalNegate(result); + } + + return result; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Homomorphic scalar multiplication failed: %s", e.what())); + } } absl::StatusOr FHEContext::HomomorphicRotate( const Ciphertext& ciphertext, int positions) const { - (void)ciphertext; // Suppress unused parameter warning - (void)positions; - - // TODO: Homomorphic rotation using OpenFHE - // - // Planned implementation: - // Ciphertext result = crypto_context_->EvalRotate(ciphertext, positions); - // return result; - // - // Note: Requires rotation keys to be generated (done in GenerateKeyPair) - // This is depth-0 (uses automorphisms, not multiplications!) - - return absl::UnimplementedError( - "FHEContext::HomomorphicRotate() - OpenFHE integration pending."); + if (!crypto_context_) { + return absl::FailedPreconditionError("Crypto context not initialized"); + } + + try { + // Homomorphic rotation - depth-0 operation! + // Uses automorphisms, not multiplications + Ciphertext result = crypto_context_->EvalRotate(ciphertext, positions); + return result; + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Homomorphic rotation failed: %s", e.what())); + } } // Accessors diff --git a/lib/crypto/fhe_context.h b/lib/crypto/fhe_context.h index a48505b..fd5427e 100644 --- a/lib/crypto/fhe_context.h +++ b/lib/crypto/fhe_context.h @@ -25,27 +25,18 @@ #include "absl/status/statusor.h" #include "absl/status/status.h" -// Forward declarations for OpenFHE types (to avoid exposing in header) -// Note: These are stub declarations for now. When OpenFHE is integrated, -// we'll include the actual OpenFHE headers. -namespace lbcrypto { - class DCRTPoly; - template class CryptoContextImpl; - template class CiphertextImpl; - template class PublicKeyImpl; - template class PrivateKeyImpl; -} +// OpenFHE headers for BGV scheme +#include "openfhe.h" namespace f2chat { -// Stub types for OpenFHE (will be replaced when OpenFHE is integrated) -// Using void* as placeholder to avoid template issues -using CryptoContext = std::shared_ptr; -using Ciphertext = std::shared_ptr; -using Plaintext = std::shared_ptr; -using PublicKey = std::shared_ptr; -using PrivateKey = std::shared_ptr; -using KeyPair = std::shared_ptr; +// Real OpenFHE types for BGV scheme +using CryptoContext = lbcrypto::CryptoContext; +using Ciphertext = lbcrypto::Ciphertext; +using Plaintext = lbcrypto::Plaintext; +using PublicKey = lbcrypto::PublicKey; +using PrivateKey = lbcrypto::PrivateKey; +using KeyPair = lbcrypto::KeyPair; // FHE key pair for a user (public key shared, private key device-held). struct FHEKeyPair { diff --git a/test/crypto/BUILD.bazel b/test/crypto/BUILD.bazel index 4e24a72..e5931f6 100644 --- a/test/crypto/BUILD.bazel +++ b/test/crypto/BUILD.bazel @@ -1,3 +1,6 @@ +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + cc_test( name = "polynomial_test", srcs = ["polynomial_test.cc"], @@ -26,3 +29,15 @@ cc_test( "@googletest//:gtest_main", ], ) + +cc_binary( + name = "fhe_benchmark", + srcs = ["fhe_benchmark.cc"], + copts = ["-fexceptions"], # OpenFHE requires exceptions + deps = [ + "//lib/crypto:encrypted_polynomial", + "//lib/crypto:polynomial", + "//lib/crypto:fhe_context", + "@google_benchmark//:benchmark_main", + ], +) diff --git a/test/crypto/encrypted_polynomial_test.cc b/test/crypto/encrypted_polynomial_test.cc index b735d42..cbbbf61 100644 --- a/test/crypto/encrypted_polynomial_test.cc +++ b/test/crypto/encrypted_polynomial_test.cc @@ -2,9 +2,7 @@ // // Tests for FHE-encrypted polynomial operations. // -// Note: These tests currently expect UnimplementedError since OpenFHE -// integration is pending. Once OpenFHE is fully integrated, these tests -// will verify homomorphic operations work correctly. +// These tests verify the OpenFHE BGV integration works correctly. #include "lib/crypto/encrypted_polynomial.h" #include "lib/crypto/polynomial.h" @@ -14,91 +12,214 @@ namespace f2chat { namespace { -// NOTE: All tests currently expect UnimplementedError -// Once OpenFHE integration is complete, these will be actual functional tests - -TEST(EncryptedPolynomialTest, FHEContextCreationPending) { - // This will fail with UnimplementedError until OpenFHE is integrated - auto fhe_context_or = FHEContext::Create(); - - // Expected: UnimplementedError (OpenFHE integration pending) - EXPECT_FALSE(fhe_context_or.ok()); - EXPECT_EQ(fhe_context_or.status().code(), absl::StatusCode::kUnimplemented); +// Test fixture for FHE tests - creates context and keys once +class EncryptedPolynomialTest : public ::testing::Test { + protected: + void SetUp() override { + auto fhe_ctx_or = FHEContext::Create(); + ASSERT_TRUE(fhe_ctx_or.ok()) << "FHE context creation failed: " + << fhe_ctx_or.status(); + fhe_ctx_ = std::make_unique(std::move(fhe_ctx_or.value())); + + auto keys_or = fhe_ctx_->GenerateKeyPair(); + ASSERT_TRUE(keys_or.ok()) << "Key generation failed: " << keys_or.status(); + keys_ = std::move(keys_or.value()); + } + + std::unique_ptr fhe_ctx_; + FHEKeyPair keys_; +}; + +TEST_F(EncryptedPolynomialTest, FHEContextCreation) { + // Verify FHE context is properly initialized + EXPECT_EQ(fhe_ctx_->ring_dimension(), RingParams::kDegree); + EXPECT_EQ(fhe_ctx_->modulus(), RingParams::kModulus); } -TEST(EncryptedPolynomialTest, EncryptionDecryptionRoundtrip_Pending) { - // TODO: Once OpenFHE is integrated, this test will verify: - // 1. Create FHE context - // 2. Generate key pair - // 3. Encrypt polynomial - // 4. Decrypt ciphertext - // 5. Verify: decrypted == original - - // For now, just document the expected test structure: - // auto fhe_ctx = FHEContext::Create().value(); - // auto keys = fhe_ctx.GenerateKeyPair().value(); - // Polynomial original({1, 2, 3, 4, 5}); - // auto encrypted = EncryptedPolynomial::Encrypt(original, keys.public_key, fhe_ctx).value(); - // auto decrypted = encrypted.Decrypt(keys.private_key, fhe_ctx).value(); - // EXPECT_EQ(decrypted, original); - - SUCCEED() << "Test structure defined, awaiting OpenFHE integration"; +TEST_F(EncryptedPolynomialTest, EncryptionDecryptionRoundtrip) { + // Test basic encryption/decryption roundtrip + std::vector coefficients = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Encrypt + auto encrypted_or = fhe_ctx_->Encrypt(coefficients, keys_.public_key); + ASSERT_TRUE(encrypted_or.ok()) << "Encryption failed: " << encrypted_or.status(); + + // Decrypt + auto decrypted_or = fhe_ctx_->Decrypt(encrypted_or.value(), keys_.private_key); + ASSERT_TRUE(decrypted_or.ok()) << "Decryption failed: " << decrypted_or.status(); + + // Verify at least the first few coefficients match + // (OpenFHE may pad the vector to batch size) + const auto& decrypted = decrypted_or.value(); + ASSERT_GE(decrypted.size(), coefficients.size()); + for (size_t i = 0; i < coefficients.size(); ++i) { + EXPECT_EQ(decrypted[i], coefficients[i]) + << "Mismatch at index " << i; + } } -TEST(EncryptedPolynomialTest, HomomorphicAddition_Pending) { - // TODO: Once OpenFHE is integrated, this test will verify: - // Enc(a) + Enc(b) == Enc(a + b) - - // Expected test: - // Polynomial a({1, 2, 3}); - // Polynomial b({4, 5, 6}); - // auto enc_a = EncryptedPolynomial::Encrypt(a, public_key, fhe_ctx).value(); - // auto enc_b = EncryptedPolynomial::Encrypt(b, public_key, fhe_ctx).value(); - // auto enc_sum = enc_a.Add(enc_b, fhe_ctx).value(); - // auto decrypted_sum = enc_sum.Decrypt(private_key, fhe_ctx).value(); - // EXPECT_EQ(decrypted_sum, a.Add(b)); - - SUCCEED() << "Homomorphic addition test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, HomomorphicAddition) { + // Verify Enc(a) + Enc(b) == Enc(a + b) + std::vector a = {1, 2, 3, 4}; + std::vector b = {10, 20, 30, 40}; + + auto enc_a = fhe_ctx_->Encrypt(a, keys_.public_key); + auto enc_b = fhe_ctx_->Encrypt(b, keys_.public_key); + ASSERT_TRUE(enc_a.ok() && enc_b.ok()); + + auto enc_sum = fhe_ctx_->HomomorphicAdd(enc_a.value(), enc_b.value()); + ASSERT_TRUE(enc_sum.ok()) << "Homomorphic add failed: " << enc_sum.status(); + + auto decrypted = fhe_ctx_->Decrypt(enc_sum.value(), keys_.private_key); + ASSERT_TRUE(decrypted.ok()); + + // Verify: a + b + for (size_t i = 0; i < a.size(); ++i) { + // Handle modular arithmetic + int64_t expected = (a[i] + b[i]) % RingParams::kModulus; + EXPECT_EQ(decrypted.value()[i], expected) + << "Addition mismatch at index " << i; + } } -TEST(EncryptedPolynomialTest, HomomorphicSubtraction_Pending) { - // TODO: Verify Enc(a) - Enc(b) == Enc(a - b) - SUCCEED() << "Homomorphic subtraction test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, HomomorphicSubtraction) { + // Verify Enc(a) - Enc(b) == Enc(a - b) + std::vector a = {100, 200, 300, 400}; + std::vector b = {10, 20, 30, 40}; + + auto enc_a = fhe_ctx_->Encrypt(a, keys_.public_key); + auto enc_b = fhe_ctx_->Encrypt(b, keys_.public_key); + ASSERT_TRUE(enc_a.ok() && enc_b.ok()); + + auto enc_diff = fhe_ctx_->HomomorphicSubtract(enc_a.value(), enc_b.value()); + ASSERT_TRUE(enc_diff.ok()) << "Homomorphic subtract failed: " << enc_diff.status(); + + auto decrypted = fhe_ctx_->Decrypt(enc_diff.value(), keys_.private_key); + ASSERT_TRUE(decrypted.ok()); + + // Verify: a - b + for (size_t i = 0; i < a.size(); ++i) { + int64_t expected = a[i] - b[i]; + EXPECT_EQ(decrypted.value()[i], expected) + << "Subtraction mismatch at index " << i; + } } -TEST(EncryptedPolynomialTest, HomomorphicScalarMultiplication_Pending) { - // TODO: Verify k * Enc(a) == Enc(k * a) - SUCCEED() << "Homomorphic scalar multiplication test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, HomomorphicScalarMultiplication) { + // Verify scalar * Enc(a) == Enc(scalar * a) + std::vector a = {1, 2, 3, 4}; + int64_t scalar = 5; + + auto enc_a = fhe_ctx_->Encrypt(a, keys_.public_key); + ASSERT_TRUE(enc_a.ok()); + + auto enc_product = fhe_ctx_->HomomorphicMultiplyScalar(enc_a.value(), scalar); + ASSERT_TRUE(enc_product.ok()) << "Scalar multiplication failed: " << enc_product.status(); + + auto decrypted = fhe_ctx_->Decrypt(enc_product.value(), keys_.private_key); + ASSERT_TRUE(decrypted.ok()); + + // Verify: scalar * a + for (size_t i = 0; i < a.size(); ++i) { + int64_t expected = (a[i] * scalar) % RingParams::kModulus; + EXPECT_EQ(decrypted.value()[i], expected) + << "Scalar multiplication mismatch at index " << i; + } } -TEST(EncryptedPolynomialTest, HomomorphicRotation_Pending) { - // TODO: Verify Rotate(Enc(a), n) == Enc(Rotate(a, n)) - SUCCEED() << "Homomorphic rotation test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, HomomorphicRotation) { + // Verify rotation works homomorphically + // Note: Rotation in packed encoding shifts the slot positions + std::vector a = {1, 2, 3, 4, 5, 6, 7, 8}; + int positions = 2; + + auto enc_a = fhe_ctx_->Encrypt(a, keys_.public_key); + ASSERT_TRUE(enc_a.ok()); + + auto enc_rotated = fhe_ctx_->HomomorphicRotate(enc_a.value(), positions); + ASSERT_TRUE(enc_rotated.ok()) << "Rotation failed: " << enc_rotated.status(); + + auto decrypted = fhe_ctx_->Decrypt(enc_rotated.value(), keys_.private_key); + ASSERT_TRUE(decrypted.ok()); + + // Rotation in packed encoding: slot i gets value from slot (i+positions) + // So position 0 gets value from original position 2, etc. + EXPECT_EQ(decrypted.value()[0], a[2]) << "Rotation check at index 0"; + EXPECT_EQ(decrypted.value()[1], a[3]) << "Rotation check at index 1"; } -TEST(EncryptedPolynomialTest, CharacterProjection_Pending) { - // TODO: Verify homomorphic character projection - // This is critical for blind routing! - SUCCEED() << "Homomorphic character projection test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, CharacterProjection) { + // Test homomorphic character projection (DFT) + // For a simple test, use character 0 (identity) which should sum all coefficients + Polynomial poly({1, 2, 3, 4, 5, 6, 7, 8}); + + auto enc_poly_or = EncryptedPolynomial::Encrypt(poly, keys_.public_key, *fhe_ctx_); + ASSERT_TRUE(enc_poly_or.ok()); + + // Project onto character 0 (identity character) + // For character 0, all Ο‡β‚€(k) = 1, so this computes (1/n) * sum of all rotations + auto projected_or = enc_poly_or.value().ProjectToCharacter(0, *fhe_ctx_); + ASSERT_TRUE(projected_or.ok()) << "Character projection failed: " + << projected_or.status(); + + auto decrypted = projected_or.value().Decrypt(keys_.private_key, *fhe_ctx_); + ASSERT_TRUE(decrypted.ok()); + + // For character 0 (identity), the projection should give a specific pattern + // Just verify the operation completes without errors for now + // Full mathematical verification would require understanding the exact DFT semantics + SUCCEED() << "Character projection completed successfully"; } -TEST(EncryptedPolynomialTest, Depth0Verification_Pending) { - // TODO: Verify all operations are depth-0 (no bootstrapping needed) - // This is a key property for efficient FHE routing - SUCCEED() << "Depth-0 verification test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, Depth0Verification) { + // Verify operations are depth-0 + // In depth-0, we can chain multiple operations without noise issues + std::vector a = {1, 2, 3, 4}; + + auto enc_a = fhe_ctx_->Encrypt(a, keys_.public_key); + ASSERT_TRUE(enc_a.ok()); + + // Chain multiple depth-0 operations + auto result = enc_a.value(); + for (int i = 0; i < 5; ++i) { + auto added = fhe_ctx_->HomomorphicAdd(result, enc_a.value()); + ASSERT_TRUE(added.ok()) << "Failed after " << i << " additions"; + result = added.value(); + } + + // Should still decrypt correctly (5 additions = 6x original) + auto decrypted = fhe_ctx_->Decrypt(result, keys_.private_key); + ASSERT_TRUE(decrypted.ok()) << "Decryption after chained ops failed"; + + for (size_t i = 0; i < a.size(); ++i) { + int64_t expected = (a[i] * 6) % RingParams::kModulus; + EXPECT_EQ(decrypted.value()[i], expected) + << "Chained addition mismatch at index " << i; + } } -// Integration test: Full encryption workflow -TEST(EncryptedPolynomialTest, FullWorkflow_Pending) { - // TODO: End-to-end test: - // 1. Alice generates key pair - // 2. Alice encrypts message for Bob (using Bob's public key) - // 3. Server performs blind routing (homomorphic operations) - // 4. Bob decrypts message (using his private key) - // 5. Verify: Bob receives correct message - // 6. Verify: Server never decrypted anything - - SUCCEED() << "Full workflow test defined, awaiting implementation"; +TEST_F(EncryptedPolynomialTest, FullWorkflow) { + // End-to-end test: encryption, operations, decryption + std::vector message = {42, 100, 255, 1000}; + int64_t scalar = 2; + + // 1. Encrypt + auto enc = fhe_ctx_->Encrypt(message, keys_.public_key); + ASSERT_TRUE(enc.ok()); + + // 2. Perform operations (simulating server routing) + auto doubled = fhe_ctx_->HomomorphicMultiplyScalar(enc.value(), scalar); + ASSERT_TRUE(doubled.ok()); + + // 3. Decrypt + auto result = fhe_ctx_->Decrypt(doubled.value(), keys_.private_key); + ASSERT_TRUE(result.ok()); + + // 4. Verify + for (size_t i = 0; i < message.size(); ++i) { + int64_t expected = (message[i] * scalar) % RingParams::kModulus; + EXPECT_EQ(result.value()[i], expected); + } } } // namespace diff --git a/test/crypto/fhe_benchmark.cc b/test/crypto/fhe_benchmark.cc new file mode 100644 index 0000000..d0c61f4 --- /dev/null +++ b/test/crypto/fhe_benchmark.cc @@ -0,0 +1,166 @@ +// test/crypto/fhe_benchmark.cc +// +// Performance benchmarks for FHE operations + +#include "lib/crypto/fhe_context.h" +#include "lib/crypto/encrypted_polynomial.h" +#include "lib/crypto/polynomial.h" +#include + +namespace f2chat { + +// Shared context and keys for benchmarks +static std::unique_ptr g_fhe_ctx; +static FHEKeyPair g_keys; +static bool g_initialized = false; + +// Setup function for all benchmarks +static void EnsureInitialized(benchmark::State& state) { + if (!g_initialized) { + auto fhe_ctx_or = FHEContext::Create(); + if (!fhe_ctx_or.ok()) { + state.SkipWithError("FHE context creation failed"); + return; + } + g_fhe_ctx = std::make_unique(std::move(fhe_ctx_or.value())); + + auto keys_or = g_fhe_ctx->GenerateKeyPair(); + if (!keys_or.ok()) { + state.SkipWithError("Key generation failed"); + return; + } + g_keys = std::move(keys_or.value()); + g_initialized = true; + } +} + +// Benchmark: Context creation +static void BM_FHEContextCreation(benchmark::State& state) { + for (auto _ : state) { + auto fhe_ctx = FHEContext::Create(); + benchmark::DoNotOptimize(fhe_ctx); + } +} +BENCHMARK(BM_FHEContextCreation); + +// Benchmark: Key generation +static void BM_KeyGeneration(benchmark::State& state) { + EnsureInitialized(state); + for (auto _ : state) { + auto keys = g_fhe_ctx->GenerateKeyPair(); + benchmark::DoNotOptimize(keys); + } +} +BENCHMARK(BM_KeyGeneration); + +// Benchmark: Encryption +static void BM_Encryption(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly({1, 2, 3, 4, 5, 6, 7, 8}); + + for (auto _ : state) { + auto enc = EncryptedPolynomial::Encrypt(poly, g_keys.public_key, *g_fhe_ctx); + benchmark::DoNotOptimize(enc); + } +} +BENCHMARK(BM_Encryption); + +// Benchmark: Decryption +static void BM_Decryption(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly({1, 2, 3, 4, 5, 6, 7, 8}); + auto enc = EncryptedPolynomial::Encrypt(poly, g_keys.public_key, *g_fhe_ctx).value(); + + for (auto _ : state) { + auto dec = enc.Decrypt(g_keys.private_key, *g_fhe_ctx); + benchmark::DoNotOptimize(dec); + } +} +BENCHMARK(BM_Decryption); + +// Benchmark: Homomorphic addition +static void BM_HomomorphicAdd(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly1({1, 2, 3, 4}); + Polynomial poly2({5, 6, 7, 8}); + auto enc1 = EncryptedPolynomial::Encrypt(poly1, g_keys.public_key, *g_fhe_ctx).value(); + auto enc2 = EncryptedPolynomial::Encrypt(poly2, g_keys.public_key, *g_fhe_ctx).value(); + + for (auto _ : state) { + auto result = enc1.Add(enc2, *g_fhe_ctx); + benchmark::DoNotOptimize(result); + } +} +BENCHMARK(BM_HomomorphicAdd); + +// Benchmark: Homomorphic subtraction +static void BM_HomomorphicSubtract(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly1({10, 20, 30, 40}); + Polynomial poly2({1, 2, 3, 4}); + auto enc1 = EncryptedPolynomial::Encrypt(poly1, g_keys.public_key, *g_fhe_ctx).value(); + auto enc2 = EncryptedPolynomial::Encrypt(poly2, g_keys.public_key, *g_fhe_ctx).value(); + + for (auto _ : state) { + auto result = enc1.Subtract(enc2, *g_fhe_ctx); + benchmark::DoNotOptimize(result); + } +} +BENCHMARK(BM_HomomorphicSubtract); + +// Benchmark: Scalar multiplication +static void BM_ScalarMultiply(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly({1, 2, 3, 4}); + auto enc = EncryptedPolynomial::Encrypt(poly, g_keys.public_key, *g_fhe_ctx).value(); + + for (auto _ : state) { + auto result = enc.MultiplyScalar(5, *g_fhe_ctx); + benchmark::DoNotOptimize(result); + } +} +BENCHMARK(BM_ScalarMultiply); + +// Benchmark: Rotation +static void BM_Rotation(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly({1, 2, 3, 4, 5, 6, 7, 8}); + auto enc = EncryptedPolynomial::Encrypt(poly, g_keys.public_key, *g_fhe_ctx).value(); + + for (auto _ : state) { + auto result = enc.Rotate(2, *g_fhe_ctx); + benchmark::DoNotOptimize(result); + } +} +BENCHMARK(BM_Rotation); + +// Benchmark: Character projection (most expensive operation) +static void BM_CharacterProjection(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly({1, 2, 3, 4, 5, 6, 7, 8}); + auto enc = EncryptedPolynomial::Encrypt(poly, g_keys.public_key, *g_fhe_ctx).value(); + + for (auto _ : state) { + auto result = enc.ProjectToCharacter(0, *g_fhe_ctx); + benchmark::DoNotOptimize(result); + } +} +BENCHMARK(BM_CharacterProjection); + +// Benchmark: Full encrypt-operate-decrypt cycle +static void BM_FullCycle(benchmark::State& state) { + EnsureInitialized(state); + Polynomial poly({1, 2, 3, 4}); + + for (auto _ : state) { + auto enc = EncryptedPolynomial::Encrypt(poly, g_keys.public_key, *g_fhe_ctx).value(); + auto doubled = enc.MultiplyScalar(2, *g_fhe_ctx).value(); + auto dec = doubled.Decrypt(g_keys.private_key, *g_fhe_ctx); + benchmark::DoNotOptimize(dec); + } +} +BENCHMARK(BM_FullCycle); + +} // namespace f2chat + +BENCHMARK_MAIN(); diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel new file mode 100644 index 0000000..fb6e6d1 --- /dev/null +++ b/third_party/BUILD.bazel @@ -0,0 +1,2 @@ +# third_party/ directory marker for Bazel +# This package contains external library build files diff --git a/third_party/cereal.BUILD b/third_party/cereal.BUILD new file mode 100644 index 0000000..f9f5c6d --- /dev/null +++ b/third_party/cereal.BUILD @@ -0,0 +1,13 @@ +# Bazel build file for cereal serialization library +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "cereal", + hdrs = glob([ + "include/cereal/**/*.hpp", + "include/cereal/**/*.h", + ]), + includes = ["include"], +) diff --git a/third_party/openfhe.BUILD b/third_party/openfhe.BUILD index ee08f4e..5f77c6b 100644 --- a/third_party/openfhe.BUILD +++ b/third_party/openfhe.BUILD @@ -1,43 +1,77 @@ # Bazel build file for OpenFHE library -# This wraps the OpenFHE CMake build for Bazel consumption +# Uses rules_foreign_cc to build OpenFHE with its native CMake build system + +load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") +load("@rules_cc//cc:cc_library.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) -# OpenFHE core library -cc_library( - name = "openfhe_core", - hdrs = glob([ - "src/core/include/**/*.h", - "src/pke/include/**/*.h", - "src/binfhe/include/**/*.h", - ]), - includes = [ - "src/core/include", - "src/pke/include", - "src/binfhe/include", +# Build all OpenFHE source files +filegroup( + name = "all_srcs", + srcs = glob(["**"]), +) + +# Build OpenFHE using CMake +cmake( + name = "openfhe_cmake", + lib_source = ":all_srcs", + out_static_libs = [ + "libOPENFHEcore_static.a", + "libOPENFHEpke_static.a", ], - srcs = glob([ - "src/core/lib/**/*.cpp", - "src/pke/lib/**/*.cpp", - ]), - copts = [ - "-std=c++17", - "-DMATHBACKEND=4", # Use NTL backend for ring operations - "-Wno-unused-parameter", - "-Wno-unused-variable", + out_include_dir = "include/openfhe", + deps = ["@cereal//:cereal"], + cache_entries = { + "CMAKE_BUILD_TYPE": "Release", + "BUILD_UNITTESTS": "OFF", + "BUILD_EXAMPLES": "OFF", + "BUILD_BENCHMARKS": "OFF", + "BUILD_EXTRAS": "OFF", + "BUILD_SHARED": "OFF", + "BUILD_STATIC": "ON", + "WITH_BE2": "OFF", + "WITH_BE4": "ON", # NTL backend + "WITH_OPENMP": "OFF", + "MATHBACKEND": "4", + "WITH_TCM": "OFF", + "GIT_SUBMOD_AUTO": "OFF", # Disable git submodule auto-download + # Enable C++ exceptions and disable strict warnings, add cereal include path + "CMAKE_CXX_FLAGS": "-fexceptions -Wno-unused-parameter -Wno-error -Wno-missing-field-initializers -I$$EXT_BUILD_DEPS$$/include", + }, + targets = [ + "OPENFHEcore_static", + "OPENFHEpke_static", ], - linkopts = ["-lntl", "-lgmp", "-lpthread"], + generate_crosstool_file = True, + # Custom build and install - skip cmake install entirely + build_args = ["-j8"], + install = False, + # Copy libs and headers manually - preserve directory structure + postfix_script = """ + set -e + mkdir -p $$INSTALLDIR$$/lib + cp lib/libOPENFHEcore_static.a $$INSTALLDIR$$/lib/ + cp lib/libOPENFHEpke_static.a $$INSTALLDIR$$/lib/ + + # Create include structure - preserve subdirectories from src/core/include + mkdir -p $$INSTALLDIR$$/include/openfhe + cp -r $$EXT_BUILD_ROOT$$/external/+_repo_rules+openfhe/src/core/include/* $$INSTALLDIR$$/include/openfhe/ + cp -r $$EXT_BUILD_ROOT$$/external/+_repo_rules+openfhe/src/pke/include/* $$INSTALLDIR$$/include/openfhe/ + cp -r $$EXT_BUILD_ROOT$$/external/+_repo_rules+openfhe/src/binfhe/include/* $$INSTALLDIR$$/include/openfhe/ + + # Copy generated config file (from build dir) + cp src/core/config_core.h $$INSTALLDIR$$/include/openfhe/ + """, ) -# OpenFHE PKE (Public Key Encryption) - for BGV/BFV/CKKS +# Convenience wrapper for OpenFHE PKE cc_library( name = "openfhe_pke", - deps = [":openfhe_core"], - hdrs = glob(["src/pke/include/**/*.h"]), - includes = ["src/pke/include"], + deps = [":openfhe_cmake"], ) -# Convenience alias for main library +# Alias for main library alias( name = "openfhe", actual = ":openfhe_pke",