Skip to content

Commit 19f67e0

Browse files
authored
Demo Flutter App (#62)
1 parent 5288f3d commit 19f67e0

37 files changed

+2341
-123
lines changed

example/.metadata

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: "ba393198430278b6595976de84fe170f553cc728"
8+
channel: "stable"
9+
10+
project_type: app
11+
12+
# Tracks metadata for the flutter migrate command
13+
migration:
14+
platforms:
15+
- platform: root
16+
create_revision: ba393198430278b6595976de84fe170f553cc728
17+
base_revision: ba393198430278b6595976de84fe170f553cc728
18+
- platform: android
19+
create_revision: ba393198430278b6595976de84fe170f553cc728
20+
base_revision: ba393198430278b6595976de84fe170f553cc728
21+
- platform: ios
22+
create_revision: ba393198430278b6595976de84fe170f553cc728
23+
base_revision: ba393198430278b6595976de84fe170f553cc728
24+
- platform: linux
25+
create_revision: ba393198430278b6595976de84fe170f553cc728
26+
base_revision: ba393198430278b6595976de84fe170f553cc728
27+
- platform: macos
28+
create_revision: ba393198430278b6595976de84fe170f553cc728
29+
base_revision: ba393198430278b6595976de84fe170f553cc728
30+
- platform: web
31+
create_revision: ba393198430278b6595976de84fe170f553cc728
32+
base_revision: ba393198430278b6595976de84fe170f553cc728
33+
- platform: windows
34+
create_revision: ba393198430278b6595976de84fe170f553cc728
35+
base_revision: ba393198430278b6595976de84fe170f553cc728
36+
37+
# User provided section
38+
39+
# List of Local paths (relative to this file) that should be
40+
# ignored by the migrate tool.
41+
#
42+
# Files that are not part of the templates will be ignored by default.
43+
unmanaged_files:
44+
- 'lib/main.dart'
45+
- 'ios/Runner.xcodeproj/project.pbxproj'

example/README.md

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
# fhel_example
1+
# Fully Homomorphic Encryption Calculator
22

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

5-
Supported OS Release: Android
5+
| Supported | Function
6+
| --- | ---
7+
| Platform | Android
8+
| Data Types | Hexadecimal, List[Int], Double, List[Double]
9+
| Schemes | BFV, BGV, CKKS
10+
| Mathematics | Add, Multiply, Subtract
11+
12+
Note: Division has open [issue](https://github.com/jeffmur/fhel/issues/55)
613

714
## Getting Started
815

@@ -24,8 +31,48 @@ make build-cmake
2431
make apk
2532
```
2633

27-
## Artifacts
34+
## Demo
35+
36+
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.
37+
38+
### Addition
39+
40+
<p float="center">
41+
<img src="./res/add/Hexadecimal.gif" width="24%" />
42+
<img src="./res/add/ListInt.gif" width="24%" />
43+
<img src="./res/add/Double.gif" width="24%" />
44+
<img src="./res/add/ListDouble.gif" width="24%" />
45+
</p>
46+
47+
### Multiplication
48+
49+
<p float="center">
50+
<img src="./res/mul/Hexadecimal.gif" width="24%" />
51+
<img src="./res/mul/ListInt.gif" width="24%" />
52+
<img src="./res/mul/Double.gif" width="24%" />
53+
<img src="./res/mul/ListDouble.gif" width="24%" />
54+
</p>
55+
56+
### Subtraction
57+
58+
Limitations:
59+
* Integer result cannot be below zero, so the resulting operation becomes MAX int modulo modulusDegree.
60+
* Cannot subtract Plaintext by Ciphertext, invalid operation.
61+
62+
<p float="center">
63+
<img src="./res/sub/Hexadecimal.gif" width="24%" />
64+
<img src="./res/sub/ListInt.gif" width="24%" />
65+
<img src="./res/sub/Double.gif" width="24%" />
66+
<img src="./res/sub/ListDouble.gif" width="24%" />
67+
</p>
68+
69+
70+
### Settings
71+
72+
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.
73+
74+
On success, a green banner will appear with 'success: valid'
75+
On failure, a red banner will appear with the corresponding error.
2876

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

31-
<img src="./res/bfv-addition.png" alt="BFV Addition Flutter App" width="50%">

example/android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ if (flutterVersionName == null) {
2323
}
2424

2525
android {
26-
namespace "com.example.fhel_example"
26+
namespace "com.example.fhel_calculator"
2727
compileSdkVersion flutter.compileSdkVersion
2828
ndkVersion "26.1.10909125"
2929

@@ -42,7 +42,7 @@ android {
4242

4343
defaultConfig {
4444
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
45-
applicationId "com.example.fhel_example"
45+
applicationId "com.example.fhel_calculator"
4646
// You can update the following values to match your application needs.
4747
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
4848
minSdkVersion flutter.minSdkVersion

example/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22
<application
3-
android:label="fhel_example"
3+
android:label="fhel_calculator"
44
android:name="${applicationName}"
55
android:icon="@mipmap/ic_launcher">
66
<activity
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.example.example
2+
3+
import io.flutter.embedding.android.FlutterActivity
4+
5+
class MainActivity: FlutterActivity()

example/android/app/src/main/kotlin/com/example/fhel_example/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.example.fhel_example
1+
package com.example.fhel_calculator
22

33
import io.flutter.embedding.android.FlutterActivity
44

example/android/settings.gradle

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ pluginManagement {
1010

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

13-
plugins {
14-
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
13+
repositories {
14+
google()
15+
mavenCentral()
16+
gradlePluginPortal()
1517
}
1618
}
1719

18-
include ":app"
20+
plugins {
21+
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22+
id "com.android.application" version "7.3.0" apply false
23+
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
24+
}
1925

20-
apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
26+
include ":app"

example/lib/addition.dart

Lines changed: 166 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,176 @@
1+
import 'package:fhel/afhe.dart';
12
import 'package:fhel/seal.dart' show Seal;
3+
import 'package:fhel_calculator/page/settings.dart';
24

3-
List<int> addVector(Map<String,int> ctx, List<int> x, List<int> add) {
4-
final fhe = Seal('bfv');
5-
fhe.genContext(ctx);
6-
fhe.genKeys();
5+
/// Conditionally add two [Plaintext] objects
6+
///
7+
/// At least one of the two integers must be encrypted.
8+
Ciphertext addCondition(SessionChanges s, Plaintext x, Plaintext add, bool xEncrypted, bool addEncrypted) {
9+
Seal fhe = s.fhe;
10+
Ciphertext cipherResult;
11+
12+
if (xEncrypted && addEncrypted) {
13+
s.log('Adding Ciphertext + Ciphertext');
14+
cipherResult = fhe.add(fhe.encrypt(x), fhe.encrypt(add));
15+
} else if (xEncrypted) {
16+
s.log('Adding Ciphertext + Plaintext');
17+
cipherResult = fhe.addPlain(fhe.encrypt(x), add);
18+
} else if (addEncrypted) {
19+
s.log('Adding Plaintext + Ciphertext');
20+
cipherResult = fhe.addPlain(fhe.encrypt(add), x);
21+
} else {
22+
throw 'Both x and add cannot be plain'; // cannot return a Ciphertext
23+
}
24+
return cipherResult;
25+
}
26+
27+
/// Add two integers
28+
///
29+
/// Encrypts, adds, and decrypts the result,
30+
String addAsHex(SessionChanges s, int x, int add, bool xEncrypted, bool addEncrypted) {
31+
Seal fhe = s.fhe;
732

8-
final pt_x = fhe.encodeVecInt(x);
9-
final ct_x = fhe.encrypt(pt_x);
33+
// Convert to Hexidecimal
34+
try {
35+
s.logSession();
36+
final start = DateTime.now();
37+
final xRadix = x.toRadixString(16);
38+
s.log('$x.toRadixString(16) => $xRadix');
39+
final plainX = fhe.plain(xRadix);
1040

11-
final pt_add = fhe.encodeVecInt(add);
12-
final ct_add = fhe.encrypt(pt_add);
41+
final addRadix = add.toRadixString(16);
42+
s.log('$add.toRadixString(16) => $addRadix');
43+
final plainAdd = fhe.plain(addRadix);
1344

14-
final ct_res = fhe.add(ct_x, ct_add);
15-
final pt_res = fhe.decrypt(ct_res);
45+
if (!xEncrypted && !addEncrypted) {
46+
s.log('Adding Plaintext + Plaintext');
47+
final result = (x + add).toString();
48+
s.log('Result: $result');
49+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
50+
return result;
51+
}
52+
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
53+
s.log('Ciphertext result size: ${cipherResult.size}');
1654

17-
return fhe.decodeVecInt(pt_res, x.length);
55+
final plainResult = fhe.decrypt(cipherResult);
56+
final result = int.parse(plainResult.text, radix: 16).toString();
57+
s.log('Decrypted result: $result');
58+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
59+
return result.toString();
60+
} catch (e) {
61+
s.log(e.toString());
62+
return e.toString();
63+
}
1864
}
1965

20-
int addInt(Map<String, int> ctx, int x, int add) {
21-
final fhe = Seal('bfv');
22-
fhe.genContext(ctx);
23-
fhe.genKeys();
66+
/// Add two doubles
67+
String addDouble(SessionChanges s, double x, double add, bool xEncrypted, bool addEncrypted) {
68+
Seal fhe = s.fhe;
2469

25-
// Convert to Hexidecimal
26-
final pt_x = fhe.plain(x.toRadixString(16));
27-
final ct_x = fhe.encrypt(pt_x);
28-
final pt_add = fhe.plain(add.toRadixString(16));
29-
final ct_add = fhe.encrypt(pt_add);
30-
final ct_res = fhe.add(ct_x, ct_add);
31-
final pt_res = fhe.decrypt(ct_res);
32-
33-
return int.parse(pt_res.text, radix: 16);
70+
if (fhe.scheme.name != 'ckks') {
71+
return '${fhe.scheme.name.toUpperCase()} does not support double addition';
72+
}
73+
74+
try {
75+
s.logSession();
76+
final start = DateTime.now();
77+
final plainX = fhe.encodeDouble(x);
78+
final plainAdd = fhe.encodeDouble(add);
79+
80+
if (!xEncrypted && !addEncrypted) {
81+
s.log('Adding Plaintext + Plaintext');
82+
final result = (x + add).toString();
83+
s.log('Result: $result');
84+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
85+
return result;
86+
}
87+
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
88+
s.log('Ciphertext result size: ${cipherResult.size}');
89+
90+
final plainResult = fhe.decrypt(cipherResult);
91+
// Generates an array of doubles filled of size (slot count)
92+
final result = fhe.decodeVecDouble(plainResult, 1).first;
93+
s.log('Decrypted result: $result');
94+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
95+
return result.toString();
96+
} catch (e) {
97+
s.log(e.toString());
98+
return e.toString();
99+
}
100+
}
101+
102+
/// Add two vectors<int>
103+
String addVectorInt(SessionChanges s, List<int> x, List<int> add, bool xEncrypted, bool addEncrypted) {
104+
Seal fhe = s.fhe;
105+
106+
if (fhe.scheme.name == 'ckks') {
107+
return 'CKKS does not support integer addition';
108+
}
109+
110+
try {
111+
s.logSession();
112+
final start = DateTime.now();
113+
final plainX = fhe.encodeVecInt(x);
114+
final plainAdd = fhe.encodeVecInt(add);
115+
116+
if (!xEncrypted && !addEncrypted) {
117+
final result = [];
118+
s.log('Adding Plaintext + Plaintext');
119+
for (int i = 0; i < x.length; i++) {
120+
result.add(x[i] + add[i]);
121+
}
122+
s.log('Result: $result');
123+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
124+
return result.join(',');
125+
}
126+
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
127+
s.log('Ciphertext result size: ${cipherResult.size}');
128+
129+
final plainResult = fhe.decrypt(cipherResult);
130+
final result = fhe.decodeVecInt(plainResult, x.length);
131+
s.log('Decrypted result: $result');
132+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
133+
return result.join(',');
134+
} catch (e) {
135+
s.log(e.toString());
136+
return e.toString();
137+
}
138+
}
139+
140+
/// Add two vectors<double>
141+
String addVectorDouble(SessionChanges s, List<double> x, List<double> add, bool xEncrypted, bool addEncrypted) {
142+
Seal fhe = s.fhe;
143+
144+
if (fhe.scheme.name != 'ckks') {
145+
return '${fhe.scheme.name.toUpperCase()} does not support double addition';
146+
}
147+
148+
try {
149+
s.logSession();
150+
final start = DateTime.now();
151+
final plainX = fhe.encodeVecDouble(x);
152+
final plainAdd = fhe.encodeVecDouble(add);
153+
154+
if (!xEncrypted && !addEncrypted) {
155+
s.log('Adding Plaintext + Plaintext');
156+
final result = [];
157+
for (int i = 0; i < x.length; i++) {
158+
result.add(x[i] + add[i]);
159+
}
160+
s.log('Result: $result');
161+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
162+
return result.join(',');
163+
}
164+
Ciphertext cipherResult = addCondition(s, plainX, plainAdd, xEncrypted, addEncrypted);
165+
s.log('Ciphertext result size: ${cipherResult.size}');
166+
167+
final plainResult = fhe.decrypt(cipherResult);
168+
final result = fhe.decodeVecDouble(plainResult, x.length);
169+
s.log('Decrypted result: $result');
170+
s.log('Elapsed: ${DateTime.now().difference(start).inMilliseconds} ms');
171+
return result.join(',');
172+
} catch (e) {
173+
s.log(e.toString());
174+
return e.toString();
175+
}
34176
}

0 commit comments

Comments
 (0)