From 3f4468dcf6af3a0ab67b19be97c96cb6887fd38e Mon Sep 17 00:00:00 2001 From: Jeffrey Murray Jr <32208127+jeffmur@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:48:59 -0800 Subject: [PATCH] =?UTF-8?q?Documentation=20=F0=9F=93=9A=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #33 --- .github/workflows/feature.yml | 20 ++- Makefile | 5 + README.md | 222 ++++----------------------- dart/CHANGELOG.md | 6 + dart/Makefile | 4 + dart/lib/afhe.dart | 158 +++++++++++++++++++ dart/lib/afhe/backend.dart | 41 +++++ dart/lib/afhe/ciphertext.dart | 44 ++++-- dart/lib/afhe/codec.dart | 28 ++-- dart/lib/afhe/crypto.dart | 47 ++++-- dart/lib/afhe/operation.dart | 30 ++-- dart/lib/afhe/plaintext.dart | 60 ++++---- dart/lib/afhe/scheme.dart | 37 +++++ dart/lib/afhe/type.dart | 56 ------- dart/lib/ffi.dart | 10 +- dart/lib/fhe.dart | 161 ------------------- dart/lib/seal.dart | 19 +++ dart/pubspec.yaml | 2 +- dart/test/openfhe/context_test.dart | 6 +- dart/test/plaintext_test.dart | 33 ---- dart/test/seal/addition_test.dart | 30 ++-- dart/test/seal/context_test.dart | 8 +- dart/test/seal/encrypt_test.dart | 16 +- dart/test/seal/plaintext_test.dart | 30 ++++ dart/test/seal/scheme_test.dart | 21 +++ dart/test/seal/subtraction_test.dart | 30 ++-- dart/test/type_test.dart | 35 ----- example/lib/addition.dart | 11 +- src/backend/Makefile | 2 - 29 files changed, 538 insertions(+), 634 deletions(-) create mode 100644 dart/lib/afhe.dart create mode 100644 dart/lib/afhe/backend.dart create mode 100644 dart/lib/afhe/scheme.dart delete mode 100644 dart/lib/afhe/type.dart delete mode 100644 dart/lib/fhe.dart create mode 100644 dart/lib/seal.dart delete mode 100644 dart/test/plaintext_test.dart create mode 100644 dart/test/seal/plaintext_test.dart create mode 100644 dart/test/seal/scheme_test.dart delete mode 100644 dart/test/type_test.dart diff --git a/.github/workflows/feature.yml b/.github/workflows/feature.yml index af762d6b..ce54478a 100644 --- a/.github/workflows/feature.yml +++ b/.github/workflows/feature.yml @@ -27,19 +27,33 @@ jobs: filters: | source: - Builder.Dockerfile - + - name: Image Tag 🏷️ if: ${{ steps.changes.outputs.source == 'true' }} id: tag run: echo ::set-output name=TAG::${{ github.sha}} - + - name: Build Image 🐳 uses: ./.github/actions/build-and-push-image if: ${{ steps.tag.outcome == 'success' }} with: image_tag: ${{ steps.tag.outputs.IMAGE_TAG }} token: ${{ secrets.GITHUB_TOKEN }} - + + docs: + name: Documentation πŸ“š + runs-on: ubuntu-latest + needs: build + container: + image: ghcr.io/jeffmur/fhel:${{ needs.build.outputs.IMAGE_TAG }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'true' + + - run: make docs + cpp-tests: name: C++ Test βš™οΈ runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index d5f1cba8..a880b672 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,11 @@ build-cmake: .PHONY: build build: trust-project build-cmake +# Generate html dart api docs +.PHONY: docs +docs: + @cd $(DART_SRC); $(MAKE) docs + # Test Abstract Layer (AFHEL) .PHONY: ctest ctest: diff --git a/README.md b/README.md index db32d432..d2ef2aa0 100644 --- a/README.md +++ b/README.md @@ -1,218 +1,52 @@ -# FHEL -The FHEL (Fully Homomorphic Encryption Library) is a powerful encryption library that enables fully homomorphic encryption (FHE) capabilities. It provides a seamless integration with Flutter, allowing developers to perform secure computations on encrypted data on resource constrained devices. +## Fully Homomorphic Encryption Library -At the core of this library is an abstract wrapper around existing FHE Libraries via AFHEL (Abstract FHE Library). The AFHEL exposes basic C functionalities to be consumed by Dart. The Dart adapter layer, FHE, acts as an consumer friendly API interface between Flutter and AFHEL. Using Object Oriented Programming, OOP, the FHE models itself as a library with basic functionalities of the desired backend library. +The Fully Homomorphic Encryption Library (fhel) is a powerful encryption library that enables fully homomorphic encryption (FHE) capabilities. It provides a seamless integration with Flutter, allowing developers to perform secure computations on encrypted data on resource constrained devices. This library aims to expose basic functionalities to Flutter users to generate encryption context, derive keys, encrypt and decrypt data, and perform arithmetic operations on encrypted data. It provides a high-level abstraction for working with plaintext and ciphertext values, as well support for multiple encryption schemes. With FHEL, developers can leverage the power of fully homomorphic encryption to perform computations on sensitive data while preserving privacy and security. It opens up new possibilities for secure data processing in applications built with Flutter. -```mermaid - ---- -title: Figure 1 - High Level Data Flow ---- - -sequenceDiagram -participant Flutter -participant FHE -participant AFHE -participant SEAL - Flutter->>FHE: Instanciate FHE("seal") - FHE->>AFHE: Pointer to Backend - AFHE->>SEAL: Instanciate SEAL - activate SEAL - SEAL->>FHE: Memory Address - Flutter->>FHE: Generate Context - FHE->>AFHE: Validate Parameters - AFHE->>SEAL: Reference SEAL - AFHE->>FHE: sucess: valid - AFHE->>FHE: [afhe_function] error: reason - Flutter->>FHE: FHE.free() - FHE->>SEAL: Free Allocated Memory - deactivate SEAL -``` - -## FHE: Implementation Layer - -Fully Homomorphic Encryption (FHE) - -The [adapter](https://refactoring.guru/design-patterns/adapter) design of this library interfaces with the abstraction layer. Using [dart:ffi](https://pub.dev/packages/ffi), Dart can execute C functions, reference memory addresses of C objects, and convert primitive data types. - -```mermaid - ---- -title: Figure 2 - Implementation Class Diagram ---- - -classDiagram -class Plaintext { - String text - Backend backend - Pointer obj - - Plaintext(Backend, String) - Plaintext(Backend, Pointer) -} +For more information, see our [wiki](https://github.com/jeffmur/fhel/wiki) -class Ciphertext { - Backend backend - Pointer library +## Development πŸ—οΈ - Ciphertext(Backend) -} +Compilation can become difficult when using multiple languages on multiple operating systems. -class Backend { - int value - String name +[Makefiles](https://www.gnu.org/software/make/manual/html_node/Introduction.html) are πŸ”‘ - Backend(String) -} +In general commands denoted with `ci` should encompass all required action(s), while others should perform a single action. -class Scheme { - int value - String name +### Compilation πŸ‘· - Scheme(String) -} +For compiliation, see backend [Makefile](./src/backend/Makefile) for library configuration. -Plaintext --> FHE -Ciphertext --> FHE -Backend --> FHE -Scheme --> FHE - -class FHE { - Backend backend - Scheme scheme - Pointer library +From the root of the project: +```bash +make build-cmake +``` - FHE(Backend) - FHE(Backend, Scheme) +### Unit Testing πŸ§ͺ - genContext(Map): void - genKeys() - encrypt(Plaintext): Ciphertext - decrypt(Ciphertext): Plaintext - invariantNoiseBudget(Ciphertext): int - encodeVecInt(List~int~): Plaintext - decodeVecInt(Plaintext, int): List~int~ - add(Ciphertext, Ciphertext): Ciphertext - addPlain(Ciphertext, Plaintext): Ciphertext - subtract(Ciphertext, Ciphertext): Ciphertext - subtractPlain(Ciphertext, Plaintext): Ciphertext -} +For C++, we have integrated [GoogleTest](https://github.com/google/googletest) as our testing framework. +From the root of the project: +```bash +make ctest ``` -**Legend**: - -* `FHE`: Models the desired backend FHE library and encryption schemas. Enables callers execute basic FHE functionalities. - -* `Plaintext`: Represents a plaintext value and contains a Pointer with the memory address of AFHE Plaintext. - -* `Ciphertext`: Represents an encrypted ciphertext value and contains a Pointer with the memory address of AFHE Ciphertext. - -* `Backend`: Contains integer and string value to convert to C Enum backend, for example `backend_t::seal`. - -* `Scheme`: Contains integer and string value to convert to C Enum scheme, for example `scheme_t::bfv`. - - -## AFHE: Abstraction Layer - -Abstract Fully Homomorphic Encryption Library (AFHE) - -The [bridge](https://refactoring.guru/design-patterns/bridge) design of this library implements an abstraction layer over existing Fully Homomorphic Encryption (FHE) libraries. Through abstraction, we can interface with various backend libraries via the same function calls. The interface layer, FHE, exposes Afhe concrete classes, ex. Aseal, lower level C function to be consumed by the Implementation Layer. Through the use of pointers, we can create/destroy/reference Afhe objects from Dart. - -```mermaid - ---- -title: Figure 3 - Abstraction Class Diagram ---- -classDiagram -Afhe <|-- SEAL - -class backend_t { - <> - SEAL - OpenFHE -} - -class scheme_t { - <> - NONE - BFV - BGV - CKKS -} - -class Plaintext{ - to_string(): String -} - -class Ciphertext{ - size(): Int -} - -scheme_t --> Afhe -Plaintext --> Afhe -Ciphertext --> Afhe - -class Afhe { - <> - scheme_t scheme - - ContextGen(scheme_t): string - KeyGen(): void - encrypt(Plaintext): void - decrypt(Ciphertext): void - invariant_noise_budget(Ciphertext): int - encode_int(vector~uint64_t~, Plaintext): void - decode_int(Plaintext, vector~uint64~): void - add(Ciphertext, Ciphertext, Ciphertext): void - subtract(Ciphertext, Ciphertext, Ciphertext): void -} - -class SEAL~Afhe~{ - setPublicKey(seal::PublicKey) - setSecretKey(seal::SecretKey) -} - -class FHE { - <> - backend_t_from_string(String): backend_t - scheme_t_from_string(String): scheme_t - init_backend(backend_t): Afhe - init_plaintext(backend_t): Plaintext - init_ciphertext(backend_t): Ciphertext - generate_context(backend_t, Afhe, scheme_t): String - generate_keys(backend_t, Afhe): void - encrypt(backend_t, Afhe, Plaintext): Ciphertext - decrypt(backend_t, Afhe, Ciphertext): Plaintext - invariant_noise_budget(backend_t, Afhe, Ciphertext): int - encode_int(backend_t, Afhe, uint64_t, int): Plaintext - decode_int(backend_t, Afhe, Plaintext): unint64 - add(backend_t, Afhe, Ciphertext, Ciphertext): Ciphertext - add_plain(backend_t, Afhe, Ciphertext, Plaintext): Ciphertext - subtract(backend_t, Afhe, Ciphertext, Ciphertext): Ciphertext - add_subtract(backend_t, Afhe, Ciphertext, Plaintext): Ciphertext -} - -Afhe --> FHE -backend_t --> FHE +For Dart, the [SDK](https://dart.dev/tools/sdk) comes out-of-the-box with a comprehensive testing framework. +From the root of the projct: +```bash +make dtest ``` -**Legend**: - -* `Afhe`: An abstract class representing the main functionality of the library. It has an integer attribute scheme_t that determines the encryption scheme(s) are supported. It provides methods for generating keys, encrypting and decrypting data, and performing addition operations. - -* `SEAL`: A concrete, refined abstraction, class that extends Afhe and represents a specific implementation of the library using the SEAL encryption scheme. It provides its own implementations of the encryption, decryption, and addition methods. +## Deployment πŸš€ -* `FHE`: An interface that defines a method get_backend for obtaining an instance of Afhe based on a given backend library type, SEAL, that will execute the appropriate function call. - -* `Plaintext` & `Ciphertext`: An abstract class representing the basic functionality of text objects. Used as the main interface for backend required parameters. - -* `backend_t`: An enumeration class representing different Fully Homomorphic Encryption libraries. +Currently configured to be manually deployed via: +```bash +dart pub publish +``` -* `scheme_t`: An enumeration class representing different encryption schemes, including BFV, BGV, and CKKS. +In the future, we may configure an [automated](https://dart.dev/tools/pub/automated-publishing) publishing. diff --git a/dart/CHANGELOG.md b/dart/CHANGELOG.md index 1bd567c4..f8be2dac 100644 --- a/dart/CHANGELOG.md +++ b/dart/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.0.3 + +Library Restructure +* Afhe: Adapter of fully homomorphic encryption. Exposes Classes +* Seal: Expose end user APIs. Extends Afhe + ## 0.0.2 Android Release diff --git a/dart/Makefile b/dart/Makefile index 5d3620cf..43f61d60 100644 --- a/dart/Makefile +++ b/dart/Makefile @@ -5,3 +5,7 @@ deps: .PHONY: test test: @dart test + +.PHONY: docs +docs: + @dart doc diff --git a/dart/lib/afhe.dart b/dart/lib/afhe.dart new file mode 100644 index 00000000..e151f7a8 --- /dev/null +++ b/dart/lib/afhe.dart @@ -0,0 +1,158 @@ +/// Abstract Fully Homomorphic Encryption +library afhe; + +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; // for Utf8 +import 'dart:io' show Directory, Platform; +import 'package:path/path.dart' as path; + +/// Components +part 'ffi.dart'; +part 'afhe/scheme.dart'; +part 'afhe/backend.dart'; +part 'afhe/plaintext.dart'; +part 'afhe/ciphertext.dart'; +part 'afhe/operation.dart'; +part 'afhe/crypto.dart'; +part 'afhe/codec.dart'; + +/// Abstract Fully Homomorphic Encryption +/// +/// It provides the ability to generate encryption contexts, keys, and perform operations on plaintexts and ciphertexts. +/// +class Afhe { + /// The encryption scheme used by the backend. + /// + /// The [Scheme] is used to represent one of the supported fhe schemes. + Scheme scheme = Scheme(); + /// The backend library used for the encryption scheme. + /// + /// The [Backend] is used to represent one of the supported fhe backends. + Backend backend = Backend(); + /// A pointer to the memory address of the underlying C++ object. + Pointer library = nullptr; + + /// Default Constructor initializes [Backend] with [Scheme]. + /// + /// The [library] stores the memory address of the underlying C++ object. + Afhe(String backendName, String schemeName) { + scheme = Scheme.set(schemeName); + backend = Backend.set(backendName); + library = _c_init_backend(backend.value); + } + + /// Generates a context for the Brakerski-Fan-Vercauteren (BFV) scheme. + String _contextBFV(Map context) { + final ptr = _c_gen_context( + backend.value, + library, + scheme.value, + context['polyModDegree'], + context['ptModBit'] ?? 0, // Only used when batching + context['ptMod'] ?? 0, // Not used when batching + context['secLevel']); + + return ptr.toDartString(); + } + + // Generates a context for the Brakerski-Gentry-Vaikuntanathan (BGV) scheme. + String _contextBGV(Map context) { + // No parameter changes + return _contextBFV(context); + } + + /// Generates a context for the Cheon-Kim-Kim-Song (CKKS) scheme. + String _contextCKKS(Map context) { + return "Not Implemented"; + } + + /// Generates a context for the encryption scheme. + /// + /// The [context] is a map of parameters used to generate the encryption context for the [Scheme]. + String genContext(Map context) { + return switch (scheme.name) { + "bfv" => _contextBFV(context), + "bgv" => _contextBGV(context), + "ckks" => _contextCKKS(context), + _ => "error: Invalid Scheme" + }; + } + + /// Generates the public and secret keys for the encryption and decryption. + void genKeys() { + _c_gen_keys(backend.value, library); + } + + /// Encrypts the plaintext message. + Ciphertext encrypt(Plaintext plaintext) { + final ptr = _c_encrypt(backend.value, library, plaintext.obj); + + Ciphertext ctx = Ciphertext(backend); + ctx.obj = ptr; + return ctx; + } + + /// Decrypts the ciphertext message. + Plaintext decrypt(Ciphertext ciphertext) { + Pointer ptr = _c_decrypt(backend.value, library, ciphertext.obj); + + Plaintext ptx = Plaintext.fromObject(backend, ptr); + return ptx; + } + + /// Returns the invariant noise budget of the ciphertext. + int invariantNoiseBudget(Ciphertext ciphertext) { + return _c_invariant_noise_budget(backend.value, library, ciphertext.obj); + } + + /// Encodes a list of integers into a plaintext. + Plaintext encodeVecInt(List vec) { + Pointer ptr = _c_encode_vector_int( + backend.value, library, intListToArray(vec), vec.length); + + return Plaintext.fromObject(backend, ptr); + } + + /// Decodes a plaintext into a list of integers. + List decodeVecInt(Plaintext plaintext, int arrayLength) { + return arrayToIntList( + _c_decode_vector_int(backend.value, library, plaintext.obj), + arrayLength); + } + + /// Adds two ciphertexts. + Ciphertext add(Ciphertext a, Ciphertext b) { + Pointer ptr = _c_add(backend.value, library, a.obj, b.obj); + + Ciphertext ctx = Ciphertext(backend); + ctx.obj = ptr; + return ctx; + } + + /// Adds a plaintext to a ciphertext. + Ciphertext addPlain(Ciphertext a, Plaintext b) { + Pointer ptr = _c_add_plain(backend.value, library, a.obj, b.obj); + + Ciphertext ctx = Ciphertext(backend); + ctx.obj = ptr; + return ctx; + } + + /// Subtracts two ciphertexts. + Ciphertext subtract(Ciphertext a, Ciphertext b) { + Pointer ptr = _c_subtract(backend.value, library, a.obj, b.obj); + + Ciphertext ctx = Ciphertext(backend); + ctx.obj = ptr; + return ctx; + } + + /// Subtracts a plaintext from a ciphertext. + Ciphertext subtractPlain(Ciphertext a, Plaintext b) { + Pointer ptr = _c_subtract_plain(backend.value, library, a.obj, b.obj); + + Ciphertext ctx = Ciphertext(backend); + ctx.obj = ptr; + return ctx; + } +} diff --git a/dart/lib/afhe/backend.dart b/dart/lib/afhe/backend.dart new file mode 100644 index 00000000..136d8d3d --- /dev/null +++ b/dart/lib/afhe/backend.dart @@ -0,0 +1,41 @@ +/// This file contains the types of supported backend libraries. +part of '../afhe.dart'; + +typedef BackendType = Int; + +typedef _StringToBackendTypeC = BackendType Function(Pointer backend); +typedef _StringToBackendType = int Function(Pointer backend); + +final _StringToBackendType _c_string_to_backend_type = dylib + .lookup>('backend_t_from_string') + .asFunction(); + +/// Represents the backend library used for the encryption scheme. +/// +/// Supported backends: +/// * Simple Encrypted Arithmetic Library (SEAL), Microsoft Research +/// +class Backend { + /// The integer value of the backend. + int value = 0; + /// The friendly name of the backend. + String name = ""; + + /// Initializes an unspecified backend. + Backend(); + + /// Initializes a backend using the provided name. + /// + /// The name is case-insensitive. Retrieves the integer value of the backend from the C++ backend. + Backend.set(String fromName) { + String lowerName = fromName.toLowerCase(); + value = _c_string_to_backend_type(lowerName.toNativeUtf8()); + name = fromName; + } +} + +typedef _InitBackendC = Pointer Function(BackendType backend); +typedef _InitBackend = Pointer Function(int backend); + +final _InitBackend _c_init_backend = + dylib.lookup>('init_backend').asFunction(); diff --git a/dart/lib/afhe/ciphertext.dart b/dart/lib/afhe/ciphertext.dart index dc65402f..ff30c043 100644 --- a/dart/lib/afhe/ciphertext.dart +++ b/dart/lib/afhe/ciphertext.dart @@ -1,27 +1,39 @@ -import 'dart:ffi'; -import 'package:fhel/afhe/type.dart' show BackendType, Backend; -import 'package:fhel/ffi.dart' show dylib; +/// This file contains the `Ciphertext` class and its associated FFI bindings. +part of '../afhe.dart'; -typedef InitCiphertextC = Pointer Function(BackendType backend); -typedef InitCiphertext = Pointer Function(int backend); +typedef _InitCiphertextC = Pointer Function(BackendType backend); +typedef _InitCiphertext = Pointer Function(int backend); -final InitCiphertext c_init_ciphertext = dylib - .lookup>('init_ciphertext') +final _InitCiphertext _c_init_ciphertext = dylib + .lookup>('init_ciphertext') .asFunction(); +/// Represents an underlying C ciphertext object. +/// +/// A ciphertext is an encrypted message. It is the output of the encryption function, +/// and the input to the decryption and operation functions. +/// class Ciphertext { - String text = ""; + /// Represents the string representation of the ciphertext. + // String text = ""; + /// Selects the [Backend] used for the encryption scheme. Backend backend = Backend(); - Pointer library = nullptr; + /// A pointer to the memory address of the underlying C++ object. + Pointer obj = nullptr; + /// Initializes a ciphertext using the provided backend. Ciphertext(this.backend) { - library = c_init_ciphertext(backend.value); + obj = _c_init_ciphertext(backend.value); } } -// void main() { -// final backend = Backend.set("seal"); -// final ciphertext = Ciphertext(backend); -// print(ciphertext.backend.name); -// print(ciphertext.text); -// } +typedef _InvariantNoiseBudgetC = Int32 Function( + BackendType backend, Pointer library, Pointer ciphertext); + +typedef _InvariantNoiseBudget = int Function( + int backend, Pointer library, Pointer ciphertext); + +/// Returns the invariant noise budget of the ciphertext. +final _InvariantNoiseBudget _c_invariant_noise_budget = dylib + .lookup>('invariant_noise_budget') + .asFunction(); diff --git a/dart/lib/afhe/codec.dart b/dart/lib/afhe/codec.dart index 60193c6e..cbda709d 100644 --- a/dart/lib/afhe/codec.dart +++ b/dart/lib/afhe/codec.dart @@ -1,19 +1,18 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; -import 'package:fhel/afhe/type.dart' show BackendType; -import 'package:fhel/ffi.dart' show dylib; +/// This file contains the FFI bindings for the encode and decode methods. +part of '../afhe.dart'; -// encode +// --- encode --- -typedef EncodeVectorIntC = Pointer Function( +typedef _EncodeVectorIntC = Pointer Function( BackendType backend, Pointer library, Pointer vec, Int size); -typedef EncodeVectorInt = Pointer Function( +typedef _EncodeVectorInt = Pointer Function( int backend, Pointer library, Pointer vec, int size); -final EncodeVectorInt c_encode_vector_int = - dylib.lookupFunction('encode_int'); +final _EncodeVectorInt _c_encode_vector_int = + dylib.lookupFunction<_EncodeVectorIntC, _EncodeVectorInt>('encode_int'); +/// Convert Dart int list to C uint64 array. Pointer intListToArray(List list) { final length = list.length; final pointer = calloc(length + 1); // +1 if null-terminated. @@ -23,17 +22,18 @@ Pointer intListToArray(List list) { return pointer; } -// decode +// --- decode --- -typedef DecodeVectorIntC = Pointer Function( +typedef _DecodeVectorIntC = Pointer Function( BackendType backend, Pointer library, Pointer plaintext); -typedef DecodeVectorInt = Pointer Function( +typedef _DecodeVectorInt = Pointer Function( int backend, Pointer library, Pointer plaintext); -final DecodeVectorInt c_decode_vector_int = - dylib.lookupFunction('decode_int'); +final _DecodeVectorInt _c_decode_vector_int = + dylib.lookupFunction<_DecodeVectorIntC, _DecodeVectorInt>('decode_int'); +/// Convert C uint64 array to Dart int list. List arrayToIntList(Pointer ptr, int length) { final list = []; for (var i = 0; i < length; i++) { diff --git a/dart/lib/afhe/crypto.dart b/dart/lib/afhe/crypto.dart index 27ac470c..3072ce7c 100644 --- a/dart/lib/afhe/crypto.dart +++ b/dart/lib/afhe/crypto.dart @@ -1,26 +1,49 @@ -import 'dart:ffi'; -// import 'package:ffi/ffi.dart'; // for Utf8 -// import 'package:fhel/afhe/plaintext.dart'; -import 'package:fhel/ffi.dart' show dylib; +/// This file contains the FFI bindings for encryption and decryption methods. +part of '../afhe.dart'; + +// --- encryption parameters --- + +typedef _GenContextC = Pointer Function( + BackendType backend, + Pointer library, + Int32 scheme, + Int64 poly_mod_degree, + Int64 pt_mod_bit, + Int64 pt_mod, + Int64 sec_level); + +typedef _GenContext = Pointer Function(int backend, Pointer library, + int scheme, int poly_mod_degree, int pt_mod_bit, int pt_mod, int sec_level); + +final _GenContext _c_gen_context = + dylib.lookup>('generate_context').asFunction(); + +// --- encryption keys --- + +typedef _GenKeysC = Void Function(BackendType backend, Pointer library); +typedef _GenKeys = void Function(int backend, Pointer library); + +final _GenKeys _c_gen_keys = + dylib.lookup>('generate_keys').asFunction(); // --- encrypt --- -typedef EncryptC = Pointer Function( +typedef _EncryptC = Pointer Function( Int backend, Pointer library, Pointer plaintext); -typedef Encrypt = Pointer Function( +typedef _Encrypt = Pointer Function( int backend, Pointer library, Pointer plaintext); -final Encrypt c_encrypt = - dylib.lookup>('encrypt').asFunction(); +final _Encrypt _c_encrypt = + dylib.lookup>('encrypt').asFunction(); // --- decrypt --- -typedef DecryptC = Pointer Function( +typedef _DecryptC = Pointer Function( Int backend, Pointer library, Pointer plaintext); -typedef Decrypt = Pointer Function( +typedef _Decrypt = Pointer Function( int backend, Pointer library, Pointer plaintext); -final Decrypt c_decrypt = - dylib.lookup>('decrypt').asFunction(); +final _Decrypt _c_decrypt = + dylib.lookup>('decrypt').asFunction(); diff --git a/dart/lib/afhe/operation.dart b/dart/lib/afhe/operation.dart index 7a7777b3..3d95a9a9 100644 --- a/dart/lib/afhe/operation.dart +++ b/dart/lib/afhe/operation.dart @@ -1,40 +1,38 @@ -import 'dart:ffi'; -// import 'package:ffi/ffi.dart'; // for Utf8 -// import 'package:fhel/afhe/plaintext.dart'; -import 'package:fhel/ffi.dart' show dylib; +/// This file contains the FFI bindinds for operations on the plaintext and ciphertext objects. +part of '../afhe.dart'; // --- add --- -typedef AddC = Pointer Function( +typedef _AddC = Pointer Function( Int backend, Pointer library, Pointer ciphertext1, Pointer ciphertext2); -typedef Add = Pointer Function( +typedef _Add = Pointer Function( int backend, Pointer library, Pointer ciphertext1, Pointer ciphertext2); -final Add c_add = dylib.lookup>('add').asFunction(); +final _Add _c_add = dylib.lookup>('add').asFunction(); -typedef AddPlainC = Pointer Function( +typedef _AddPlainC = Pointer Function( Int backend, Pointer library, Pointer ciphertext, Pointer plaintext); -typedef AddPlain = Pointer Function( +typedef _AddPlain = Pointer Function( int backend, Pointer library, Pointer ciphertext, Pointer plaintext); -final AddPlain c_add_plain = dylib.lookup>('add_plain').asFunction(); +final _AddPlain _c_add_plain = dylib.lookup>('add_plain').asFunction(); // --- subtract --- -typedef SubC = Pointer Function( +typedef _SubC = Pointer Function( Int backend, Pointer library, Pointer ciphertext1, Pointer ciphertext2); -typedef Sub = Pointer Function( +typedef _Sub = Pointer Function( int backend, Pointer library, Pointer ciphertext1, Pointer ciphertext2); -final Sub c_subtract = dylib.lookup>('subtract').asFunction(); +final _Sub _c_subtract = dylib.lookup>('subtract').asFunction(); -typedef SubPlainC = Pointer Function( +typedef _SubPlainC = Pointer Function( Int backend, Pointer library, Pointer ciphertext, Pointer plaintext); -typedef SubPlain = Pointer Function( +typedef _SubPlain = Pointer Function( int backend, Pointer library, Pointer ciphertext, Pointer plaintext); -final SubPlain c_subtract_plain = dylib.lookup>('subtract_plain').asFunction(); +final _SubPlain _c_subtract_plain = dylib.lookup>('subtract_plain').asFunction(); diff --git a/dart/lib/afhe/plaintext.dart b/dart/lib/afhe/plaintext.dart index 0b0a4af9..743bff0d 100644 --- a/dart/lib/afhe/plaintext.dart +++ b/dart/lib/afhe/plaintext.dart @@ -1,56 +1,50 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; // for Utf8 -import 'package:fhel/ffi.dart' show dylib; -import 'package:fhel/afhe/type.dart' show BackendType, Backend; +/// This file contains the `Ciphertext` class and its associated FFI bindings. +part of '../afhe.dart'; -typedef InitPlaintextC = Pointer Function(BackendType backend); -typedef InitPlaintext = Pointer Function(int backend); +typedef _InitPlaintextC = Pointer Function(BackendType backend); +typedef _InitPlaintext = Pointer Function(int backend); -final InitPlaintext c_init_plaintext = - dylib.lookup>('init_plaintext').asFunction(); +final _InitPlaintext _c_init_plaintext = + dylib.lookup>('init_plaintext').asFunction(); -typedef InitPlaintextValueC = Pointer Function( - BackendType backend, Pointer text); -typedef InitPlaintextValue = Pointer Function(int backend, Pointer text); +typedef _InitPlaintextValueC = Pointer Function(BackendType backend, Pointer text); +typedef _InitPlaintextValue = Pointer Function(int backend, Pointer text); -final InitPlaintextValue c_init_plaintext_value = dylib - .lookup>('init_plaintext_value') +final _InitPlaintextValue _c_init_plaintext_value = dylib + .lookup>('init_plaintext_value') .asFunction(); -typedef GetPlaintextC = Pointer Function(Pointer plaintext); -typedef GetPlaintext = Pointer Function(Pointer plaintext); +typedef _PlaintextC = Pointer Function(Pointer plaintext); -final GetPlaintext c_get_plaintext = dylib - .lookup>('get_plaintext_value') +final _PlaintextC _c_get_plaintext = dylib + .lookup>('get_plaintext_value') .asFunction(); +/// Represents an underlying C plaintext object. +/// +/// A plaintext is a message that has not been encrypted. It is the input to the encryption and +/// operation functions, and the output of the decryption function. +/// class Plaintext { + /// Represents the value of the plaintext. String text = ""; + /// Selects the [Backend] used for the encryption scheme. Backend backend = Backend(); + /// A pointer to the memory address of the underlying C++ object. Pointer obj = nullptr; + /// Initializes a plaintext using the provided backend. Plaintext(this.backend) { - obj = c_init_plaintext(backend.value); + obj = _c_init_plaintext(backend.value); } + /// Initializes a plaintext C [obj] with [text] using the provided [Backend]. Plaintext.withValue(this.backend, this.text) { - // print(obj); - obj = c_init_plaintext_value(backend.value, text.toNativeUtf8()); - // print(obj); + obj = _c_init_plaintext_value(backend.value, text.toNativeUtf8()); } + /// Initializes a plaintext from an existing C [obj]. Plaintext.fromObject(this.backend, this.obj) { - text = c_get_plaintext(obj).toDartString(); + text = _c_get_plaintext(obj).toDartString(); } } - -// void main() { -// final backend = Backend.set("seal"); -// final plaintext = Plaintext(backend); -// print(plaintext.backend.name); -// print(plaintext.text); - -// final plaintext2 = Plaintext.withValue(backend, "1234"); -// print(plaintext2.backend.name); -// print(plaintext2.text); -// } diff --git a/dart/lib/afhe/scheme.dart b/dart/lib/afhe/scheme.dart new file mode 100644 index 00000000..b960158e --- /dev/null +++ b/dart/lib/afhe/scheme.dart @@ -0,0 +1,37 @@ +/// This files contains types of schemes supported by backend libraries +part of '../afhe.dart'; + +typedef SchemeType = Int; + +typedef _StringtoSchemeTypeC = SchemeType Function(Pointer scheme); +typedef _StringToSchemeType = int Function(Pointer scheme); + +final _StringToSchemeType _c_string_to_scheme_type = dylib + .lookup>('scheme_t_from_string') + .asFunction(); + +/// Represents the supported schemes used by the backend. +/// +/// Supported schemes: +/// * Brakerski-Fan-Vercauteren (BFV) scheme supports integer arithmetic, ciphertext coefficent uses Most Significant Bits (MSB). +/// * Brakerski-Gentry-Vaikuntanathan (BGV) scheme supports integer arithmetic, ciphertext coefficent uses Least Significant Bits (LSB). +/// * Cheon-Kim-Kim-Song (CKKS) scheme supports approximate real-number arithmetic. +/// +class Scheme { + /// The integer value of the scheme. + int value = 0; + /// The friendly name of the scheme. + String name = ""; + + /// Initializes an unspecified scheme. + Scheme(); + + /// Initializes a scheme using the provided name. + /// + /// The name is case-insensitive. Retrieves the integer value of the scheme from the C++ backend. + Scheme.set(String fromName) { + String lowerName = fromName.toLowerCase(); + value = _c_string_to_scheme_type(lowerName.toNativeUtf8()); + name = fromName; + } +} diff --git a/dart/lib/afhe/type.dart b/dart/lib/afhe/type.dart deleted file mode 100644 index f6136bf0..00000000 --- a/dart/lib/afhe/type.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; // for Utf8 -import 'package:fhel/ffi.dart' show dylib; - -// ------------------ Backend FHE Libraries ------------------ -typedef BackendType = Int; - -typedef StringToBackendTypeC = BackendType Function(Pointer backend); -typedef StringToBackendType = int Function(Pointer backend); - -final StringToBackendType c_string_to_backend_type = dylib - .lookup>('backend_t_from_string') - .asFunction(); - -class Backend { - int value = 0; - String name = ""; - - Backend(); - - Backend.set(String fromName) { - String lowerName = fromName.toLowerCase(); - value = c_string_to_backend_type(lowerName.toNativeUtf8()); - name = fromName; - } -} - -typedef InitBackendC = Pointer Function(BackendType backend); -typedef InitBackend = Pointer Function(int backend); - -final InitBackend c_init_backend = - dylib.lookup>('init_backend').asFunction(); - -// --- Types of Schemes supported by Backend Libraries --- - -typedef SchemeType = Int; - -typedef StringtoSchemeTypeC = SchemeType Function(Pointer scheme); -typedef StringToSchemeType = int Function(Pointer scheme); - -final StringToSchemeType c_string_to_scheme_type = dylib - .lookup>('scheme_t_from_string') - .asFunction(); - -class Scheme { - int value = 0; - String name = ""; - - Scheme(); - - Scheme.set(String fromName) { - String lowerName = fromName.toLowerCase(); - value = c_string_to_scheme_type(lowerName.toNativeUtf8()); - name = fromName; - } -} diff --git a/dart/lib/ffi.dart b/dart/lib/ffi.dart index ab4ce26c..66530678 100644 --- a/dart/lib/ffi.dart +++ b/dart/lib/ffi.dart @@ -1,16 +1,16 @@ -import 'dart:ffi'; -import 'dart:io' show Directory, Platform; -import 'package:path/path.dart' as path; +part of '../afhe.dart'; const String _libName = 'fhel'; +/// Interop with the Abstract C FHE library via [dart:ffi](https://pub.dev/packages/ffi). final DynamicLibrary dylib = () { var pathPrefix = ""; - // Development + // Development (test) // * Build from project root (parent of dart folder) if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) { pathPrefix = path.join(Directory.current.parent.path, 'build'); } + // Release (pub) if (Platform.isIOS || Platform.isMacOS) { return DynamicLibrary.open(path.join(pathPrefix, 'lib$_libName.dylib')); } @@ -21,4 +21,4 @@ final DynamicLibrary dylib = () { return DynamicLibrary.open('$_libName.dll'); } throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); -}(); \ No newline at end of file +}(); diff --git a/dart/lib/fhe.dart b/dart/lib/fhe.dart deleted file mode 100644 index 417be36b..00000000 --- a/dart/lib/fhe.dart +++ /dev/null @@ -1,161 +0,0 @@ -library fhe; - -import 'dart:ffi'; -import 'package:fhel/afhe/codec.dart'; -import 'package:fhel/afhe/crypto.dart'; -import 'package:fhel/ffi.dart' show dylib; -import 'package:ffi/ffi.dart'; // for Utf8 -import 'package:fhel/afhe/type.dart'; -import 'package:fhel/afhe/plaintext.dart'; -import 'package:fhel/afhe/ciphertext.dart'; -import 'package:fhel/afhe/operation.dart'; - -typedef GenContextC = Pointer Function( - BackendType backend, - Pointer library, - Int32 scheme, - Int64 poly_mod_degree, - Int64 pt_mod_bit, - Int64 pt_mod, - Int64 sec_level); - -typedef GenContext = Pointer Function(int backend, Pointer library, - int scheme, int poly_mod_degree, int pt_mod_bit, int pt_mod, int sec_level); - -final GenContext c_gen_context = - dylib.lookup>('generate_context').asFunction(); - -typedef GenKeysC = Void Function(BackendType backend, Pointer library); -typedef GenKeys = void Function(int backend, Pointer library); - -final GenKeys c_gen_keys = - dylib.lookup>('generate_keys').asFunction(); - -typedef InvariantNoiseBudgetC = Int32 Function( - BackendType backend, Pointer library, Pointer ciphertext); - -typedef InvariantNoiseBudget = int Function( - int backend, Pointer library, Pointer ciphertext); - -final InvariantNoiseBudget c_invariant_noise_budget = dylib - .lookup>('invariant_noise_budget') - .asFunction(); - -class FHE { - Scheme scheme = Scheme(); - Backend backend = Backend(); - Pointer library = nullptr; - String publicKey = ""; - String secretKey = ""; - - FHE(String name) { - scheme = Scheme.set(name); - backend = Backend.set(name); - library = c_init_backend(backend.value); - } - - FHE.withScheme(String backendName, String schemeName) { - scheme = Scheme.set(schemeName); - backend = Backend.set(backendName); - library = c_init_backend(backend.value); - } - - String genBFVContext(Map context) { - final ptr = c_gen_context( - backend.value, - library, - scheme.value, - context['polyModDegree'], - context['ptModBit'] ?? 0, // Only used when batching - context['ptMod'] ?? 0, // Not used when batching - context['secLevel']); - - return ptr.toDartString(); - } - - String genBGVContext(Map context) { - // No parameter changes - return genBFVContext(context); - } - - String genCKKSContext(Map context) { - return "Not Implemented"; - } - - String genContext(Map context) { - return switch (scheme.name) { - "bfv" => genBFVContext(context), - "bgv" => genBGVContext(context), - "ckks" => genCKKSContext(context), - _ => "error: Invalid Scheme" - }; - } - - void genKeys() { - c_gen_keys(backend.value, library); - } - - Ciphertext encrypt(Plaintext plaintext) { - final ptr = c_encrypt(backend.value, library, plaintext.obj); - - Ciphertext ctx = Ciphertext(backend); - ctx.library = ptr; - return ctx; - } - - Plaintext decrypt(Ciphertext ciphertext) { - Pointer ptr = c_decrypt(backend.value, library, ciphertext.library); - - Plaintext ptx = Plaintext.fromObject(backend, ptr); - return ptx; - } - - int invariantNoiseBudget(Ciphertext ciphertext) { - return c_invariant_noise_budget(backend.value, library, ciphertext.library); - } - - Plaintext encodeVecInt(List vec) { - Pointer ptr = c_encode_vector_int( - backend.value, library, intListToArray(vec), vec.length); - - return Plaintext.fromObject(backend, ptr); - } - - List decodeVecInt(Plaintext plaintext, int arrayLength) { - return arrayToIntList( - c_decode_vector_int(backend.value, library, plaintext.obj), - arrayLength); - } - - Ciphertext add(Ciphertext a, Ciphertext b) { - Pointer ptr = c_add(backend.value, library, a.library, b.library); - - Ciphertext ctx = Ciphertext(backend); - ctx.library = ptr; - return ctx; - } - - Ciphertext addPlain(Ciphertext a, Plaintext b) { - Pointer ptr = c_add_plain(backend.value, library, a.library, b.obj); - - Ciphertext ctx = Ciphertext(backend); - ctx.library = ptr; - return ctx; - } - - Ciphertext subtract(Ciphertext a, Ciphertext b) { - Pointer ptr = c_subtract(backend.value, library, a.library, b.library); - - Ciphertext ctx = Ciphertext(backend); - ctx.library = ptr; - return ctx; - } - - Ciphertext subtractPlain(Ciphertext a, Plaintext b) { - Pointer ptr = c_subtract_plain(backend.value, library, a.library, b.obj); - - Ciphertext ctx = Ciphertext(backend); - ctx.library = ptr; - return ctx; - } -} diff --git a/dart/lib/seal.dart b/dart/lib/seal.dart new file mode 100644 index 00000000..1f984b8f --- /dev/null +++ b/dart/lib/seal.dart @@ -0,0 +1,19 @@ +/// Microsoft SEAL +library seal; + +import 'package:fhel/afhe.dart'; + +/// Expose basic functionalities of Microsoft SEAL. +/// +/// β€œMicrosoft SEAL (Release 4.1),” January 2023. https://github.com/Microsoft/SEAL. +/// +class Seal extends Afhe { + /// Instanciates SEAL [Backend] with [Scheme] + Seal(String scheme) : super('seal', scheme); + + /// Generate a [Plaintext] object from a string. + Plaintext plain(String value) => Plaintext.withValue(backend, value); + + /// Generate a empty [Ciphertext] object. + Ciphertext cipher() => Ciphertext(backend); +} diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml index 7123066b..22090e3a 100644 --- a/dart/pubspec.yaml +++ b/dart/pubspec.yaml @@ -1,6 +1,6 @@ name: fhel description: Dart library for Fully Homomorphic Encryption -version: 0.0.2 +version: 0.0.3 repository: https://github.com/jeffmur/fhel environment: diff --git a/dart/test/openfhe/context_test.dart b/dart/test/openfhe/context_test.dart index 0bb9be7a..af499ee4 100644 --- a/dart/test/openfhe/context_test.dart +++ b/dart/test/openfhe/context_test.dart @@ -1,13 +1,13 @@ import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; +import 'package:fhel/afhe.dart' show Afhe; void main() { test('Not Implemented', () { - final fhe = FHE("openfhe"); + final fhe = Afhe("openfhe", "foo"); final ctx = fhe.genContext({}); expect(ctx, "error: Invalid Scheme"); - final fhe_val = FHE.withScheme("openfhe", "bfv"); + final fhe_val = Afhe("openfhe", "bfv"); final ctx_val = fhe_val.genContext({'polyModDegree': 4096, 'ptMod': 1024, 'secLevel': 128}); diff --git a/dart/test/plaintext_test.dart b/dart/test/plaintext_test.dart deleted file mode 100644 index e2e16207..00000000 --- a/dart/test/plaintext_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'dart:ffi'; -import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; -import 'package:fhel/afhe/plaintext.dart'; - -void main() { - group('Microsoft SEAL', () { - final fhe = FHE('seal'); - - test('Default Constructor', () { - final seal_pt = Plaintext(fhe.backend); - expect(seal_pt.text, ""); - expect(seal_pt.backend.name, 'seal'); - // Reference a new AsealPlaintext - expect(seal_pt.obj.address, isNot(nullptr)); - }); - - test('Constructor with Integer', () { - final seal_pt_val = Plaintext.withValue(fhe.backend, "123"); - expect(seal_pt_val.text, "123"); - expect(seal_pt_val.backend.name, fhe.backend.name); - expect(seal_pt_val.obj.address, isNot(nullptr)); - }); - - test('Constructor with Hexadecimal', () { - String hex = 123.toRadixString(16); /* 7b */ - final seal_pt_val = Plaintext.withValue(fhe.backend, hex); - expect(seal_pt_val.text, "7b"); - expect(seal_pt_val.backend.name, fhe.backend.name); - expect(seal_pt_val.obj.address, isNot(nullptr)); - }); - }); -} diff --git a/dart/test/seal/addition_test.dart b/dart/test/seal/addition_test.dart index 1ba76a66..4c0a44e4 100644 --- a/dart/test/seal/addition_test.dart +++ b/dart/test/seal/addition_test.dart @@ -1,18 +1,16 @@ import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; -import 'package:fhel/afhe/plaintext.dart'; +import 'package:fhel/seal.dart' show Seal; -// Least Significant Bit (LSB), Most Significant Bit (MSB) const schemes = ['bgv', 'bfv']; -String sealAddition(String scheme, String a, String b, Map ctx, {bool encryptAddend=true}) { - final fhe = FHE.withScheme('seal', scheme); +String add(String scheme, String a, String b, Map ctx, {bool encryptAddend=true}) { + final fhe = Seal(scheme); String status = fhe.genContext(ctx); expect(status, 'success: valid'); fhe.genKeys(); - final pt_x = Plaintext.withValue(fhe.backend, a); + final pt_x = fhe.plain(a); final ct_x = fhe.encrypt(pt_x); - final pt_add = Plaintext.withValue(fhe.backend, b); + final pt_add = fhe.plain(b); // Optionally, addend can be plaintext var ct_res; @@ -34,8 +32,8 @@ void main() { 'secLevel': 128 }; for (var sch in schemes) { - expect('280', sealAddition(sch, '200', '80', ctx)); - expect('280', sealAddition(sch, '200', '80', ctx, encryptAddend: false)); + expect('280', add(sch, '200', '80', ctx)); + expect('280', add(sch, '200', '80', ctx, encryptAddend: false)); } }); @@ -49,18 +47,18 @@ void main() { for (var sch in schemes) { expect( 280.toRadixString(16), - sealAddition(sch, 200.toRadixString(16), 80.toRadixString(16), ctx)); + add(sch, 200.toRadixString(16), 80.toRadixString(16), ctx)); expect( 280.toRadixString(16), - sealAddition(sch, 200.toRadixString(16), 80.toRadixString(16), ctx, encryptAddend: false)); + add(sch, 200.toRadixString(16), 80.toRadixString(16), ctx, encryptAddend: false)); } // Otherwise, you are forced to increase plaintext modulus, // resulting in more noise growth and failed decryption ctx['ptMod'] = 67136; for (var sch in schemes) { - expect('4000', sealAddition(sch, '2000', '2000', ctx)); - expect('4000', sealAddition(sch, '2000', '2000', ctx, encryptAddend: false)); + expect('4000', add(sch, '2000', '2000', ctx)); + expect('4000', add(sch, '2000', '2000', ctx, encryptAddend: false)); } // When using hexadecimal, plain modulus can be lower @@ -68,16 +66,16 @@ void main() { for (var sch in schemes) { expect( 2800.toRadixString(16).toLowerCase(), - sealAddition(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx)); + add(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx)); expect( 2800.toRadixString(16).toLowerCase(), - sealAddition(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx, encryptAddend: false)); + add(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx, encryptAddend: false)); } }); test("List Addition", () { for (var sch in schemes) { - final fhe = FHE.withScheme('seal', sch); + final fhe = Seal(sch); Map ctx = { 'polyModDegree': 8192, 'ptModBit': 20, diff --git a/dart/test/seal/context_test.dart b/dart/test/seal/context_test.dart index 41a911cd..adeabd8c 100644 --- a/dart/test/seal/context_test.dart +++ b/dart/test/seal/context_test.dart @@ -1,5 +1,5 @@ import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; +import 'package:fhel/seal.dart' show Seal; // Least Significant Bit (LSB), Most Significant Bit (MSB) const schemes = ['bgv', 'bfv']; @@ -7,7 +7,7 @@ const schemes = ['bgv', 'bfv']; void main() { test('Valid Parameters', () { for (var sch in schemes) { - final fhe = FHE.withScheme("seal", sch); + final fhe = Seal(sch); final context = fhe .genContext({'polyModDegree': 4096, 'ptMod': 1024, 'secLevel': 128}); expect(context, "success: valid"); @@ -16,7 +16,7 @@ void main() { test('Invalid Plaintext Mod Degree', () { for (var sch in schemes) { - final fhe = FHE.withScheme("seal", sch); + final fhe = Seal(sch); final context = fhe.genContext({'polyModDegree': 0, 'ptMod': 1024, 'secLevel': 128}); expect(context, "invalid_argument: non-standard poly_modulus_degree"); @@ -27,7 +27,7 @@ void main() { // (e.g. 1024, 2048, 4096, 8192, 16384, or 32768). test('Invalid Plaintext Mod Bit Count', () { for (var sch in schemes) { - final fhe = FHE.withScheme("seal", sch); + final fhe = Seal(sch); final context = fhe.genContext({'polyModDegree': 4096, 'ptMod': 0, 'secLevel': 128}); expect( diff --git a/dart/test/seal/encrypt_test.dart b/dart/test/seal/encrypt_test.dart index 1b3378d9..2095b0b6 100644 --- a/dart/test/seal/encrypt_test.dart +++ b/dart/test/seal/encrypt_test.dart @@ -1,6 +1,5 @@ import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; -import 'package:fhel/afhe/plaintext.dart'; +import 'package:fhel/seal.dart' show Seal; // Least Significant Bit (LSB), Most Significant Bit (MSB) const schemes = ['bgv', 'bfv']; @@ -8,15 +7,15 @@ const schemes = ['bgv', 'bfv']; void main() { test('Encrypt Hexidecimal', () { for (var sch in schemes) { - final fhe = FHE.withScheme("seal", sch); - final context = fhe - .genContext({'polyModDegree': 4096, 'ptMod': 4096, 'secLevel': 128}); + final fhe = Seal(sch); + final context = fhe.genContext( + {'polyModDegree': 4096, 'ptMod': 4096, 'secLevel': 128}); expect(context, "success: valid"); fhe.genKeys(); int pt_int = 2500; String pt = pt_int.toRadixString(16); - final plaintext = Plaintext.withValue(fhe.backend, pt); + final plaintext = fhe.plain(pt); final ciphertext = fhe.encrypt(plaintext); final decrypted = fhe.decrypt(ciphertext); @@ -33,9 +32,10 @@ void main() { test('Encrypt List', () { for (var sch in schemes) { - final fhe = FHE.withScheme("seal", sch); - fhe.genContext( + final fhe = Seal(sch); + final context = fhe.genContext( {'polyModDegree': 8192, 'ptModBit': 20, 'ptMod': 0, 'secLevel': 128}); + expect(context, "success: valid"); fhe.genKeys(); List vec = [1, 2, 3, 4]; diff --git a/dart/test/seal/plaintext_test.dart b/dart/test/seal/plaintext_test.dart new file mode 100644 index 00000000..04f0b9e4 --- /dev/null +++ b/dart/test/seal/plaintext_test.dart @@ -0,0 +1,30 @@ +import 'dart:ffi'; +import 'package:test/test.dart'; +import 'package:fhel/seal.dart' show Seal; + +void main() { + final fhe = Seal(''); + + test('Default Constructor', () { + final seal_pt = fhe.plain(""); + expect(seal_pt.text, ""); + expect(seal_pt.backend.name, 'seal'); + // Reference a new AsealPlaintext + expect(seal_pt.obj.address, isNot(nullptr)); + }); + + test('Constructor with Integer', () { + final seal_pt_val = fhe.plain("123"); + expect(seal_pt_val.text, "123"); + expect(seal_pt_val.backend.name, fhe.backend.name); + expect(seal_pt_val.obj.address, isNot(nullptr)); + }); + + test('Constructor with Hexadecimal', () { + String hex = 123.toRadixString(16); /* 7b */ + final seal_pt_val = fhe.plain(hex); + expect(seal_pt_val.text, "7b"); + expect(seal_pt_val.backend.name, fhe.backend.name); + expect(seal_pt_val.obj.address, isNot(nullptr)); + }); +} diff --git a/dart/test/seal/scheme_test.dart b/dart/test/seal/scheme_test.dart new file mode 100644 index 00000000..3273d0d2 --- /dev/null +++ b/dart/test/seal/scheme_test.dart @@ -0,0 +1,21 @@ +import 'package:test/test.dart'; +import 'package:fhel/afhe.dart' show Afhe; + +void main() { + test('Schemes are case in-senstitive', () { + for (var name in ['BFV', 'BfV', 'bFv', 'bfv']) { + final fhe = Afhe('seal', name); + expect(fhe.scheme.value, 1); + expect(fhe.scheme.name, name); + } + }); + + test('Schemes supports in-order', () { + final schemes = {1: 'BFV', 2: 'CKKS', 3: 'BGV'}; + schemes.forEach((k, v) { + final fhe = Afhe('seal', v); + expect(fhe.scheme.value, k); + expect(fhe.scheme.name, v); + }); + }); +} diff --git a/dart/test/seal/subtraction_test.dart b/dart/test/seal/subtraction_test.dart index 46b690a6..e7fe456c 100644 --- a/dart/test/seal/subtraction_test.dart +++ b/dart/test/seal/subtraction_test.dart @@ -1,18 +1,16 @@ import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; -import 'package:fhel/afhe/plaintext.dart'; +import 'package:fhel/seal.dart' show Seal; -// Least Significant Bit (LSB), Most Significant Bit (MSB) const schemes = ['bgv', 'bfv']; -String sealSubtraction(String scheme, String a, String b, Map ctx, {bool encryptSubtrahend = true}) { - final fhe = FHE.withScheme('seal', scheme); +String sub(String scheme, String a, String b, Map ctx, {bool encryptSubtrahend = true}) { + final fhe = Seal(scheme); String status = fhe.genContext(ctx); expect(status, 'success: valid'); fhe.genKeys(); - final pt_x = Plaintext.withValue(fhe.backend, a); + final pt_x = fhe.plain(a); final ct_x = fhe.encrypt(pt_x); - final pt_sub = Plaintext.withValue(fhe.backend, b); + final pt_sub = fhe.plain(b); var ct_res; if (encryptSubtrahend) { @@ -33,8 +31,8 @@ void main() { 'secLevel': 128 }; for (var sch in schemes) { - expect('0', sealSubtraction(sch, '100', '100', ctx)); - expect('0', sealSubtraction(sch, '100', '100', ctx, encryptSubtrahend: false)); + expect('0', sub(sch, '100', '100', ctx)); + expect('0', sub(sch, '100', '100', ctx, encryptSubtrahend: false)); } }); @@ -48,18 +46,18 @@ void main() { for (var sch in schemes) { expect( 120.toRadixString(16), - sealSubtraction(sch, 200.toRadixString(16), 80.toRadixString(16), ctx)); + sub(sch, 200.toRadixString(16), 80.toRadixString(16), ctx)); expect( 120.toRadixString(16), - sealSubtraction(sch, 200.toRadixString(16), 80.toRadixString(16), ctx, encryptSubtrahend: false)); + sub(sch, 200.toRadixString(16), 80.toRadixString(16), ctx, encryptSubtrahend: false)); } // Otherwise, you are forced to increase plaintext modulus, // resulting in more noise growth and failed decryption ctx['ptMod'] = 67136; for (var sch in schemes) { - expect('0', sealSubtraction(sch, '2000', '2000', ctx)); - expect('0', sealSubtraction(sch, '2000', '2000', ctx, encryptSubtrahend: false)); + expect('0', sub(sch, '2000', '2000', ctx)); + expect('0', sub(sch, '2000', '2000', ctx, encryptSubtrahend: false)); } // When using hexadecimal, plain modulus can be lower @@ -67,16 +65,16 @@ void main() { for (var sch in schemes) { expect( 1200.toRadixString(16).toLowerCase(), - sealSubtraction(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx)); + sub(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx)); expect( 1200.toRadixString(16).toLowerCase(), - sealSubtraction(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx, encryptSubtrahend: false)); + sub(sch, 2000.toRadixString(16), 800.toRadixString(16), ctx, encryptSubtrahend: false)); } }); test("SEAL List Subtraction", () { for (var sch in schemes) { - final fhe = FHE.withScheme('seal', sch); + final fhe = Seal(sch); Map ctx = { 'polyModDegree': 8192, 'ptModBit': 20, diff --git a/dart/test/type_test.dart b/dart/test/type_test.dart deleted file mode 100644 index b074e80e..00000000 --- a/dart/test/type_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:test/test.dart'; -import 'package:fhel/fhe.dart' show FHE; - -void main() { - group('Backend:', () { - test('Microsoft SEAL', () { - final fhe = FHE('seal'); - expect(fhe.backend.value, 1); - expect(fhe.backend.name, 'seal'); - }); - - test('OpenFHE', () { - final fhe = FHE('openfhe'); - expect(fhe.backend.value, 2); - expect(fhe.backend.name, 'openfhe'); - }); - }); - - test('Schema are case in-senstitive', () { - for (var name in ['BFV', 'BfV', 'bFv', 'bfv']) { - final fhe = FHE.withScheme('seal', name); - expect(fhe.scheme.value, 1); - expect(fhe.scheme.name, name); - } - }); - - test('Seal Schemes supports in-order', () { - final schemes = {1: 'BFV', 2: 'CKKS', 3: 'BGV'}; - schemes.forEach((k, v) { - final fhe = FHE.withScheme('seal', v); - expect(fhe.scheme.value, k); - expect(fhe.scheme.name, v); - }); - }); -} diff --git a/example/lib/addition.dart b/example/lib/addition.dart index 66db04f2..c09f9b5d 100644 --- a/example/lib/addition.dart +++ b/example/lib/addition.dart @@ -1,8 +1,7 @@ -import 'package:fhel/fhe.dart' show FHE; -import 'package:fhel/afhe/plaintext.dart'; +import 'package:fhel/seal.dart' show Seal; List addVector(Map ctx, List x, List add) { - final fhe = FHE.withScheme('seal', 'bfv'); + final fhe = Seal('bfv'); fhe.genContext(ctx); fhe.genKeys(); @@ -19,14 +18,14 @@ List addVector(Map ctx, List x, List add) { } int addInt(Map ctx, int x, int add) { - final fhe = FHE.withScheme('seal', 'bfv'); + final fhe = Seal('bfv'); fhe.genContext(ctx); fhe.genKeys(); // Convert to Hexidecimal - final pt_x = Plaintext.withValue(fhe.backend, x.toRadixString(16)); + final pt_x = fhe.plain(x.toRadixString(16)); final ct_x = fhe.encrypt(pt_x); - final pt_add = Plaintext.withValue(fhe.backend, add.toRadixString(16)); + final pt_add = fhe.plain(add.toRadixString(16)); final ct_add = fhe.encrypt(pt_add); final ct_res = fhe.add(ct_x, ct_add); final pt_res = fhe.decrypt(ct_res); diff --git a/src/backend/Makefile b/src/backend/Makefile index fe39b3fd..72a739e1 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -14,8 +14,6 @@ trust: @git config --global --add safe.directory $(INSTALL_DIR)/thirdparty/zlib-src @git config --global --add safe.directory $(INSTALL_DIR)/thirdparty/zstd-src -# TODO: How can we install dependencies in CMakeLists.txt? - .PHONY: build build: trust @echo "Building seal...$(INSTALL_DIR)"