Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demo Flutter App #62

Merged
merged 33 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c287acb
Settings page
jeffmur Mar 12, 2024
06e40a3
Finalize parameter page
jeffmur Mar 13, 2024
c8404dc
Rm initialValue
jeffmur Mar 13, 2024
35c63f4
Hexidecimal Addition
jeffmur Mar 13, 2024
437e41c
Logging
jeffmur Mar 14, 2024
af5612e
some rearranging
jeffmur Mar 14, 2024
56ff3a4
more re-arranging
jeffmur Mar 14, 2024
942c08a
Bug when changing context for a single text
jeffmur Mar 14, 2024
4189ecf
Run multiple tests, clears logs
jeffmur Mar 15, 2024
6384215
formatting
jeffmur Mar 15, 2024
9214205
List<double>
jeffmur Mar 15, 2024
7956d98
addDouble
jeffmur Mar 15, 2024
f77ab5e
Condense Hexadecimal to one page
jeffmur Mar 16, 2024
93fbe6e
Double Calculator
jeffmur Mar 27, 2024
27b6a9c
List Integer / Double
jeffmur Mar 27, 2024
d0ba88e
List[int]
jeffmur Mar 27, 2024
4cf1409
List[double]
jeffmur Mar 27, 2024
c67b66f
Reduce operation conditional boilerplate 🍝
jeffmur Mar 27, 2024
bc66159
Hex + Double report error
jeffmur Mar 27, 2024
ecb1ba6
Rounded precision for List[double]
jeffmur Mar 27, 2024
74640f0
Correct parameter form behavior
jeffmur Mar 28, 2024
ff3a837
Settings Page Demo
jeffmur Mar 28, 2024
9b6a221
Consolidate success/fail banner
jeffmur Mar 28, 2024
ac5948c
fhel_example -> fhel_calculator
jeffmur Mar 28, 2024
bfde35b
can we persist form fields?
jeffmur Mar 29, 2024
7978d7f
Persist form after submission 🎉
jeffmur Mar 29, 2024
b407b61
Enhance logging + demo addition
jeffmur Mar 29, 2024
b4727b8
fix double
jeffmur Mar 29, 2024
e616525
multiplication demo
jeffmur Mar 29, 2024
97c463d
update README
jeffmur Mar 29, 2024
b792246
add demo brief
jeffmur Mar 29, 2024
c9f44ee
subtraction demo
jeffmur Mar 29, 2024
8abf795
Merge branch 'main' into demo
jeffmur Apr 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions example/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "ba393198430278b6595976de84fe170f553cc728"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: android
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: ios
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: linux
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: macos
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: web
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: windows
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
57 changes: 52 additions & 5 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# fhel_example
# Fully Homomorphic Encryption Calculator

Demonstrates how to use the fhel plugin integrates into an Flutter Application.

Supported OS Release: Android
| Supported | Function
| --- | ---
| Platform | Android
| Data Types | Hexadecimal, List[Int], Double, List[Double]
| Schemes | BFV, BGV, CKKS
| Mathematics | Add, Multiply, Subtract

Note: Division has open [issue](https://github.com/jeffmur/fhel/issues/55)

## Getting Started

Expand All @@ -24,8 +31,48 @@ make build-cmake
make apk
```

## Artifacts
## Demo

FHE Calculator aims to demonstrate how the basic mathematical operations behave with various supported data types. Using the Encrypt toggle, the input value may be handled as Plaintext / Ciphertext.

### Addition

<p float="center">
<img src="./res/add/Hexadecimal.gif" width="24%" />
<img src="./res/add/ListInt.gif" width="24%" />
<img src="./res/add/Double.gif" width="24%" />
<img src="./res/add/ListDouble.gif" width="24%" />
</p>

### Multiplication

<p float="center">
<img src="./res/mul/Hexadecimal.gif" width="24%" />
<img src="./res/mul/ListInt.gif" width="24%" />
<img src="./res/mul/Double.gif" width="24%" />
<img src="./res/mul/ListDouble.gif" width="24%" />
</p>

### Subtraction

Limitations:
* Integer result cannot be below zero, so the resulting operation becomes MAX int modulo modulusDegree.
* Cannot subtract Plaintext by Ciphertext, invalid operation.

<p float="center">
<img src="./res/sub/Hexadecimal.gif" width="24%" />
<img src="./res/sub/ListInt.gif" width="24%" />
<img src="./res/sub/Double.gif" width="24%" />
<img src="./res/sub/ListDouble.gif" width="24%" />
</p>


### Settings

To configure Microsoft SEAL, there are 3 supported schemes: BFV, BGV, and CKKS. If you'd like, there are default parameters available to get started. Toggling Default Parameters, will autofill default parameters. Once Validate is clicked, the parameters will be verified and keys will be generated.

On success, a green banner will appear with 'success: valid'
On failure, a red banner will appear with the corresponding error.

In the provided code sample, [addition.dart](./lib/addition.dart), we expose `addInt` and `addVector`.
<img src="./res/SettingsPage.gif" alt="SettingsPage" width="25%">

<img src="./res/bfv-addition.png" alt="BFV Addition Flutter App" width="50%">
4 changes: 2 additions & 2 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if (flutterVersionName == null) {
}

android {
namespace "com.example.fhel_example"
namespace "com.example.fhel_calculator"
compileSdkVersion flutter.compileSdkVersion
ndkVersion "26.1.10909125"

Expand All @@ -42,7 +42,7 @@ android {

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.fhel_example"
applicationId "com.example.fhel_calculator"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
Expand Down
2 changes: 1 addition & 1 deletion example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="fhel_example"
android:label="fhel_calculator"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.example

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity()
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.fhel_example
package com.example.fhel_calculator

import io.flutter.embedding.android.FlutterActivity

Expand Down
14 changes: 10 additions & 4 deletions example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ pluginManagement {

includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")

plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}

include ":app"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}

apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
include ":app"
190 changes: 166 additions & 24 deletions example/lib/addition.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1,176 @@
import 'package:fhel/afhe.dart';
import 'package:fhel/seal.dart' show Seal;
import 'package:fhel_calculator/page/settings.dart';

List<int> addVector(Map<String,int> ctx, List<int> x, List<int> add) {
final fhe = Seal('bfv');
fhe.genContext(ctx);
fhe.genKeys();
/// Conditionally add two [Plaintext] objects
///
/// At least one of the two integers must be encrypted.
Ciphertext addCondition(SessionChanges s, Plaintext x, Plaintext add, bool xEncrypted, bool addEncrypted) {
Seal fhe = s.fhe;
Ciphertext cipherResult;

if (xEncrypted && addEncrypted) {
s.log('Adding Ciphertext + Ciphertext');
cipherResult = fhe.add(fhe.encrypt(x), fhe.encrypt(add));
} else if (xEncrypted) {
s.log('Adding Ciphertext + Plaintext');
cipherResult = fhe.addPlain(fhe.encrypt(x), add);
} else if (addEncrypted) {
s.log('Adding Plaintext + Ciphertext');
cipherResult = fhe.addPlain(fhe.encrypt(add), x);
} else {
throw 'Both x and add cannot be plain'; // cannot return a Ciphertext
}
return cipherResult;
}

/// Add two integers
///
/// Encrypts, adds, and decrypts the result,
String addAsHex(SessionChanges s, int x, int add, bool xEncrypted, bool addEncrypted) {
Seal fhe = s.fhe;

final pt_x = fhe.encodeVecInt(x);
final ct_x = fhe.encrypt(pt_x);
// Convert to Hexidecimal
try {
s.logSession();
final start = DateTime.now();
final xRadix = x.toRadixString(16);
s.log('$x.toRadixString(16) => $xRadix');
final plainX = fhe.plain(xRadix);

final pt_add = fhe.encodeVecInt(add);
final ct_add = fhe.encrypt(pt_add);
final addRadix = add.toRadixString(16);
s.log('$add.toRadixString(16) => $addRadix');
final plainAdd = fhe.plain(addRadix);

final ct_res = fhe.add(ct_x, ct_add);
final pt_res = fhe.decrypt(ct_res);
if (!xEncrypted && !addEncrypted) {
s.log('Adding Plaintext + Plaintext');
final result = (x + add).toString();
s.log('Result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result;
}
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
s.log('Ciphertext result size: ${cipherResult.size}');

return fhe.decodeVecInt(pt_res, x.length);
final plainResult = fhe.decrypt(cipherResult);
final result = int.parse(plainResult.text, radix: 16).toString();
s.log('Decrypted result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result.toString();
} catch (e) {
s.log(e.toString());
return e.toString();
}
}

int addInt(Map<String, int> ctx, int x, int add) {
final fhe = Seal('bfv');
fhe.genContext(ctx);
fhe.genKeys();
/// Add two doubles
String addDouble(SessionChanges s, double x, double add, bool xEncrypted, bool addEncrypted) {
Seal fhe = s.fhe;

// Convert to Hexidecimal
final pt_x = fhe.plain(x.toRadixString(16));
final ct_x = fhe.encrypt(pt_x);
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);

return int.parse(pt_res.text, radix: 16);
if (fhe.scheme.name != 'ckks') {
return '${fhe.scheme.name.toUpperCase()} does not support double addition';
}

try {
s.logSession();
final start = DateTime.now();
final plainX = fhe.encodeDouble(x);
final plainAdd = fhe.encodeDouble(add);

if (!xEncrypted && !addEncrypted) {
s.log('Adding Plaintext + Plaintext');
final result = (x + add).toString();
s.log('Result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result;
}
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
s.log('Ciphertext result size: ${cipherResult.size}');

final plainResult = fhe.decrypt(cipherResult);
// Generates an array of doubles filled of size (slot count)
final result = fhe.decodeVecDouble(plainResult, 1).first;
s.log('Decrypted result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result.toString();
} catch (e) {
s.log(e.toString());
return e.toString();
}
}

/// Add two vectors<int>
String addVectorInt(SessionChanges s, List<int> x, List<int> add, bool xEncrypted, bool addEncrypted) {
Seal fhe = s.fhe;

if (fhe.scheme.name == 'ckks') {
return 'CKKS does not support integer addition';
}

try {
s.logSession();
final start = DateTime.now();
final plainX = fhe.encodeVecInt(x);
final plainAdd = fhe.encodeVecInt(add);

if (!xEncrypted && !addEncrypted) {
final result = [];
s.log('Adding Plaintext + Plaintext');
for (int i = 0; i < x.length; i++) {
result.add(x[i] + add[i]);
}
s.log('Result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result.join(',');
}
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
s.log('Ciphertext result size: ${cipherResult.size}');

final plainResult = fhe.decrypt(cipherResult);
final result = fhe.decodeVecInt(plainResult, x.length);
s.log('Decrypted result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result.join(',');
} catch (e) {
s.log(e.toString());
return e.toString();
}
}

/// Add two vectors<double>
String addVectorDouble(SessionChanges s, List<double> x, List<double> add, bool xEncrypted, bool addEncrypted) {
Seal fhe = s.fhe;

if (fhe.scheme.name != 'ckks') {
return '${fhe.scheme.name.toUpperCase()} does not support double addition';
}

try {
s.logSession();
final start = DateTime.now();
final plainX = fhe.encodeVecDouble(x);
final plainAdd = fhe.encodeVecDouble(add);

if (!xEncrypted && !addEncrypted) {
s.log('Adding Plaintext + Plaintext');
final result = [];
for (int i = 0; i < x.length; i++) {
result.add(x[i] + add[i]);
}
s.log('Result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result.join(',');
}
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
s.log('Ciphertext result size: ${cipherResult.size}');

final plainResult = fhe.decrypt(cipherResult);
final result = fhe.decodeVecDouble(plainResult, x.length);
s.log('Decrypted result: $result');
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
return result.join(',');
} catch (e) {
s.log(e.toString());
return e.toString();
}
}
Loading