diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..e1b09e8
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,19 @@
+{
+ "bloc.newCubitTemplate.type": "equatable",
+ "psi-header.config": {
+ "blankLinesAfter": 0,
+ "forceToTop": true,
+ },
+ "psi-header.templates": [
+ {
+ "language": "*",
+ "template": [
+ "Copyright 2019-<> <>",
+ "",
+ "Use of this source code is governed by an MIT-style",
+ "license that can be found in the LICENSE file or at",
+ "https://opensource.org/licenses/MIT.",
+ ]
+ }
+ ],
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 507b7ec..c01bf49 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
Fast and powerful cryptographic functions for Flutter.
@@ -35,34 +35,127 @@ For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB**
In short, NativeCrypto is incomparable with PointyCastle.
+## Features
+
+* Hash functions
+ - SHA-256
+ - SHA-384
+ - SHA-512
+* HMAC functions
+ - HMAC-SHA-256
+ - HMAC-SHA-384
+ - HMAC-SHA-512
+* Secure random
+* PBKDF2
+* AES
+ - Uint8List encryption/decryption
+ - File encryption/decryption
+
+## Quick start
+
+```dart
+import 'package:native_crypto/native_crypto.dart';
+
+Future main() async {
+ // Message to encrypt
+ final Uint8List message = 'Hello World!'.toBytes();
+
+ // Ask user for a password
+ final String password = await getPassword();
+
+ // Initialize a PBKDF2 object
+ final Pbkdf2 pbkdf2 = Pbkdf2(
+ length: 32, // 32 bytes
+ iterations: 1000,
+ salt: 'salt'.toBytes(),
+ hashAlgorithm: HashAlgorithm.sha256,
+ );
+
+ // Derive a secret key from the password
+ final SecretKey secretKey = await pbkdf2(password: password);
+
+ // Initialize an AES cipher
+ final AES cipher = AES(
+ key: secretKey,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+ );
+
+ // Encrypt the message
+ final CipherText cipherText = await cipher.encrypt(message);
+
+ // Decrypt the message
+ final Uint8List decryptedMessage = await cipher.decrypt(cipherText);
+
+ // Verify and print the decrypted message
+ assert(listEquals(message, decryptedMessage));
+
+ print(decryptedMessage.toStr());
+}
+```
+
+Check the [example](./native_crypto/example) for a complete example.
+
+Please take a look a the compatibility table below to check if your target is supported.
+
+> Note: This **Flutter** example must run on a real device or a simulator.
+
## Usage
+#### Compatibility
+
First, check compatibility with your targets.
| iOS | Android | MacOS | Linux | Windows | Web |
| --- | ------- | ----- | ----- | ------- | --- |
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
+> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md))
+
+NativeCrypto ciphertexts are formatted as follow:
+
+```
++------------------+--------------------+------------------+
+| Nonce (12 bytes) | Cipher text (n-28) | Tag (16 bytes) |
++------------------+--------------------+------------------+
+```
+
+> Warning: If your data comes from another source, make sur to use the same format.
+
#### Hash
-To digest a message, you can use the following function:
+To digest a message, you'll need to initialize a Hasher object implementing `Hash` . Then, you can digest your message.
```dart
-Uint8List hash = await HashAlgorithm.sha256.digest(message);
+Hash hasher = Sha256();
+Uint8List digest = await hasher.digest(message);
```
> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512
-#### Keys
+#### HMAC
-You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random.
+To generate a HMAC, you'll need to initialize a `Hmac` object. Then, you can generate a HMAC from a message and a secret key.
```dart
-SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]));
+Hmac hmac = HmacSha256();
+Uint8List hmac = await hmac.digest(message, secretKey);
+```
+
+> In NativeCrypto, you can use the following HMAC functions: HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512
+
+#### Keys
+
+You can build a `SecretKey` from utf8, utf16, base64, base16 (hex) strings, int list or raw bytes. You can also generate a SecretKey from secure random.
+
+```dart
+SecretKey secretKey = SecretKey(bytes); // bytes is a Uint8List
SecretKey secretKey = SecretKey.fromUtf8('secret');
+SecretKet secretKey = SecretKey.fromUtf16('secret');
SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
SecretKey secretKey = SecretKey.fromBase16('63657274');
-SecretKey secretKey = await SecretKey.fromSecureRandom(256);
+SecretKey secretKey = SecretKey.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]);
+SecretKey secretKey = await SecretKey.fromSecureRandom(32); // 32 bytes
```
#### Key derivation
@@ -72,20 +165,21 @@ You can derive a `SecretKey` using **PBKDF2**.
First, you need to initialize a `Pbkdf2` object.
```dart
-Pbkdf2 pbkdf2 = Pbkdf2(
- keyBytesCount: 32,
+final Pbkdf2 pbkdf2 = Pbkdf2(
+ length: 32, // 32 bytes
iterations: 1000,
- algorithm: HashAlgorithm.sha512,
+ salt: salt.toBytes(),
+ hashAlgorithm: HashAlgorithm.sha256,
);
```
-Then, you can derive a `SecretKey` from a password and salt.
+Then, you can derive a `SecretKey` from a password.
```dart
-SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt');
+SecretKey secretKey = await pbkdf2(password: password);
```
-> In NativeCrypto, you can use the following key derivation function: PBKDF2
+> Note: Pbkdf2 is a callable class. You can use it like a function.
#### Cipher
@@ -94,40 +188,93 @@ And now, you can use the `SecretKey` to encrypt/decrypt a message.
First, you need to initialize a `Cipher` object.
```dart
-AES cipher = AES(secretKey);
+final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+);
```
Then, you can encrypt your message.
```dart
-CipherTextWrapper wrapper = await cipher.encrypt(message);
-
-CipherText cipherText = wrapper.unwrap();
-// same as
-CipherText cipherText = wrapper.single;
-
-// or
-
-List cipherTexts = wrapper.unwrap>();
-// same as
-List cipherTexts = wrapper.list;
+final CipherText cipherText = await cipher.encrypt(message);
```
-After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured.
+After an encryption you obtain a `CipherText` which contains chunks. You can get the underlying bytes with `cipherText.bytes` .
-Uppon receiving encrypted message, you can decrypt it.
-You have to reconstruct the wrapper before decrypting.
+Uppon receiving encrypted message `receivedData` , you can decrypt it.
+You have to reconstruct the ciphertext and the setup the chunk factory.
```dart
-CipherTextWrapper wrapper = CipherTextWrapper.fromBytes(
- data,
- ivLength: AESMode.gcm.ivLength,
- tagLength: AESMode.gcm.tagLength,
-);
+final CipherText receivedCipherText CipherText(
+ receivedData,
+ chunkFactory: (bytes) => AESCipherChunk(
+ bytes,
+ ivLength: cipher.mode.ivLength,
+ tagLength: cipher.mode.tagLength,
+ ),
+),
```
Then, you can decrypt your message.
```dart
-Uint8List message = await cipher.decrypt(wrapper);
-```
\ No newline at end of file
+Uint8List message = await cipher.decrypt(receivedCipherText);
+```
+
+#### Files
+
+You can encrypt/decrypt files.
+
+First, you need to initialize a `Cipher` object.
+
+```dart
+final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+);
+```
+
+Then, you can encrypt your file.
+
+```dart
+await cipher.encryptFile(plainText, cipherText);
+```
+
+> Note: `plainText` and `cipherText` are `File` objects.
+
+You can decrypt your file.
+
+```dart
+await cipher.decryptFile(cipherText, plainText);
+```
+
+#### Advanced
+
+You can force the use of a specific IV. Please note that the IV must be unique for each encryption.
+
+```dart
+final CipherText cipherText = await cipher.encryptWithIV(message, iv);
+```
+
+⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing.
+
+## Development
+
+### Android
+
+> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2b-add-android-platform-code-ktjava
+
+* Launch Android Studio.
+* Select Open an existing Android Studio Project in the Welcome to Android Studio dialog, or select File > Open from the menu, and select the `packages/native_crypto/example/android/build.gradle` file.
+* In the Gradle Sync dialog, select OK.
+* In the Android Gradle Plugin Update dialog, select Don’t remind me again for this project.
+
+### iOS
+
+> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2c-add-ios-platform-code-swifthm
+
+* Launch Xcode.
+* Select File > Open, and select the `packages/native_crypto/example/ios/Runner.xcworkspace` file.
diff --git a/packages/native_crypto/LICENSE b/packages/native_crypto/LICENSE
index 082d930..67b1d53 100644
--- a/packages/native_crypto/LICENSE
+++ b/packages/native_crypto/LICENSE
@@ -2,7 +2,7 @@ NativeCrypto
MIT License
-Copyright (c) 2019 - 2022 Hugo Pointcheval
+Copyright (c) 2019 - 2023 Hugo Pointcheval
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+SOFTWARE.
diff --git a/packages/native_crypto/README.md b/packages/native_crypto/README.md
index 507b7ec..f412888 100644
--- a/packages/native_crypto/README.md
+++ b/packages/native_crypto/README.md
@@ -1,133 +1,3 @@
-
-
-
Fast and powerful cryptographic functions for Flutter.
-
+# NativeCrypto
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----
-
-[[Changelog]](./CHANGELOG.md) | [[License]](./LICENSE)
-
----
-
-## About
-
-The goal of this plugin is to provide a fast and powerful cryptographic functions by calling native libraries. On Android, it uses [javax.cypto](https://developer.android.com/reference/javax/crypto/package-summary), and on iOS, it uses [CommonCrypto](https://opensource.apple.com/source/CommonCrypto/) and [CryptoKit](https://developer.apple.com/documentation/cryptokit/)
-
-I started this projet because I wanted to add cryptographic functions on a Flutter app. But I faced a problem with the well-known [Pointy Castle](https://pub.dev/packages/pointycastle) library: the performance was very poor. Here some benchmarks and comparison:
-
-
-
-For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB** in **~5.6s** with PointyCastle and in **~40ms** with NativeCrypto. And on an *OnePlus 5*, you can encrypt/decrypt a message of **50MiB** in **~6min30** with PointyCastle and in less than **~1s** with NativeCrypto.
-
-In short, NativeCrypto is incomparable with PointyCastle.
-
-## Usage
-
-First, check compatibility with your targets.
-
-| iOS | Android | MacOS | Linux | Windows | Web |
-| --- | ------- | ----- | ----- | ------- | --- |
-| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
-
-#### Hash
-
-To digest a message, you can use the following function:
-
-```dart
-Uint8List hash = await HashAlgorithm.sha256.digest(message);
-```
-
-> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512
-
-#### Keys
-
-You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random.
-
-```dart
-SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]));
-SecretKey secretKey = SecretKey.fromUtf8('secret');
-SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
-SecretKey secretKey = SecretKey.fromBase16('63657274');
-SecretKey secretKey = await SecretKey.fromSecureRandom(256);
-```
-
-#### Key derivation
-
-You can derive a `SecretKey` using **PBKDF2**.
-
-First, you need to initialize a `Pbkdf2` object.
-
-```dart
-Pbkdf2 pbkdf2 = Pbkdf2(
- keyBytesCount: 32,
- iterations: 1000,
- algorithm: HashAlgorithm.sha512,
-);
-```
-
-Then, you can derive a `SecretKey` from a password and salt.
-
-```dart
-SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt');
-```
-
-> In NativeCrypto, you can use the following key derivation function: PBKDF2
-
-#### Cipher
-
-And now, you can use the `SecretKey` to encrypt/decrypt a message.
-
-First, you need to initialize a `Cipher` object.
-
-```dart
-AES cipher = AES(secretKey);
-```
-
-Then, you can encrypt your message.
-
-```dart
-CipherTextWrapper wrapper = await cipher.encrypt(message);
-
-CipherText cipherText = wrapper.unwrap();
-// same as
-CipherText cipherText = wrapper.single;
-
-// or
-
-List cipherTexts = wrapper.unwrap>();
-// same as
-List cipherTexts = wrapper.list;
-```
-
-After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured.
-
-Uppon receiving encrypted message, you can decrypt it.
-You have to reconstruct the wrapper before decrypting.
-
-```dart
-CipherTextWrapper wrapper = CipherTextWrapper.fromBytes(
- data,
- ivLength: AESMode.gcm.ivLength,
- tagLength: AESMode.gcm.tagLength,
-);
-```
-
-Then, you can decrypt your message.
-
-```dart
-Uint8List message = await cipher.decrypt(wrapper);
-```
\ No newline at end of file
+Readme available at [project root](../../README.md).
diff --git a/packages/native_crypto/analysis_options.yaml b/packages/native_crypto/analysis_options.yaml
index db48808..82177cd 100644
--- a/packages/native_crypto/analysis_options.yaml
+++ b/packages/native_crypto/analysis_options.yaml
@@ -1 +1 @@
-include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml
\ No newline at end of file
+include: package:wyatt_analysis/analysis_options.flutter.yaml
\ No newline at end of file
diff --git a/packages/native_crypto/example/.metadata b/packages/native_crypto/example/.metadata
index ee7f61d..92fd7de 100644
--- a/packages/native_crypto/example/.metadata
+++ b/packages/native_crypto/example/.metadata
@@ -1,10 +1,30 @@
# 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.
+# This file should be version controlled.
version:
- revision: cf4400006550b70f28e4b4af815151d1e74846c6
+ revision: cd41fdd495f6944ecd3506c21e94c6567b073278
channel: stable
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
+ base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
+ - platform: web
+ create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
+ base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
+
+ # 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'
diff --git a/packages/native_crypto/example/analysis_options.yaml b/packages/native_crypto/example/analysis_options.yaml
index 61b6c4d..82177cd 100644
--- a/packages/native_crypto/example/analysis_options.yaml
+++ b/packages/native_crypto/example/analysis_options.yaml
@@ -1,29 +1 @@
-# This file configures the analyzer, which statically analyzes Dart code to
-# check for errors, warnings, and lints.
-#
-# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
-# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
-# invoked from the command line by running `flutter analyze`.
-
-# The following line activates a set of recommended lints for Flutter apps,
-# packages, and plugins designed to encourage good coding practices.
-include: package:flutter_lints/flutter.yaml
-
-linter:
- # The lint rules applied to this project can be customized in the
- # section below to disable rules from the `package:flutter_lints/flutter.yaml`
- # included above or to enable additional rules. A list of all available lints
- # and their documentation is published at
- # https://dart-lang.github.io/linter/lints/index.html.
- #
- # Instead of disabling a lint rule for the entire project in the
- # section below, it can also be suppressed for a single line of code
- # or a specific dart file by using the `// ignore: name_of_lint` and
- # `// ignore_for_file: name_of_lint` syntax on the line or in the file
- # producing the lint.
- rules:
- # avoid_print: false # Uncomment to disable the `avoid_print` rule
- # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
+include: package:wyatt_analysis/analysis_options.flutter.yaml
\ No newline at end of file
diff --git a/packages/native_crypto/example/android/app/build.gradle b/packages/native_crypto/example/android/app/build.gradle
index 15632c2..237aa05 100644
--- a/packages/native_crypto/example/android/app/build.gradle
+++ b/packages/native_crypto/example/android/app/build.gradle
@@ -57,6 +57,7 @@ android {
signingConfig signingConfigs.debug
}
}
+ namespace 'fr.pointcheval.native_crypto_example'
}
flutter {
diff --git a/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml
index 94e68f6..f880684 100644
--- a/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml
+++ b/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
+
diff --git a/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml
index c55f004..7f62b9c 100644
--- a/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
+
+
diff --git a/packages/native_crypto/example/android/build.gradle b/packages/native_crypto/example/android/build.gradle
index 3245887..4bcbcdd 100644
--- a/packages/native_crypto/example/android/build.gradle
+++ b/packages/native_crypto/example/android/build.gradle
@@ -6,7 +6,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.2.0'
+ classpath 'com.android.tools.build:gradle:7.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties
index 562c5e4..cc5527d 100644
--- a/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
diff --git a/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist b/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist
index 8d4492f..9625e10 100644
--- a/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 9.0
+ 11.0
diff --git a/packages/native_crypto/example/ios/Podfile.lock b/packages/native_crypto/example/ios/Podfile.lock
index 3257d42..cad8cae 100644
--- a/packages/native_crypto/example/ios/Podfile.lock
+++ b/packages/native_crypto/example/ios/Podfile.lock
@@ -1,21 +1,74 @@
PODS:
+ - DKImagePickerController/Core (4.3.4):
+ - DKImagePickerController/ImageDataManager
+ - DKImagePickerController/Resource
+ - DKImagePickerController/ImageDataManager (4.3.4)
+ - DKImagePickerController/PhotoGallery (4.3.4):
+ - DKImagePickerController/Core
+ - DKPhotoGallery
+ - DKImagePickerController/Resource (4.3.4)
+ - DKPhotoGallery (0.0.17):
+ - DKPhotoGallery/Core (= 0.0.17)
+ - DKPhotoGallery/Model (= 0.0.17)
+ - DKPhotoGallery/Preview (= 0.0.17)
+ - DKPhotoGallery/Resource (= 0.0.17)
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Core (0.0.17):
+ - DKPhotoGallery/Model
+ - DKPhotoGallery/Preview
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Model (0.0.17):
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Preview (0.0.17):
+ - DKPhotoGallery/Model
+ - DKPhotoGallery/Resource
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Resource (0.0.17):
+ - SDWebImage
+ - SwiftyGif
+ - file_picker (0.0.1):
+ - DKImagePickerController/PhotoGallery
+ - Flutter
- Flutter (1.0.0)
- native_crypto_ios (0.0.1):
- Flutter
+ - SDWebImage (5.15.5):
+ - SDWebImage/Core (= 5.15.5)
+ - SDWebImage/Core (5.15.5)
+ - SwiftyGif (5.4.4)
DEPENDENCIES:
+ - file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`)
+SPEC REPOS:
+ trunk:
+ - DKImagePickerController
+ - DKPhotoGallery
+ - SDWebImage
+ - SwiftyGif
+
EXTERNAL SOURCES:
+ file_picker:
+ :path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
native_crypto_ios:
:path: ".symlinks/plugins/native_crypto_ios/ios"
SPEC CHECKSUMS:
- Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+ DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
+ DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
+ file_picker: ce3938a0df3cc1ef404671531facef740d03f920
+ Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d
+ SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
+ SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
diff --git a/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj b/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj
index 8c503eb..47f5593 100644
--- a/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -200,6 +200,7 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -214,6 +215,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -340,7 +342,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -418,7 +420,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -467,7 +469,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/packages/native_crypto/example/ios/Runner/Info.plist b/packages/native_crypto/example/ios/Runner/Info.plist
index 8293c48..c8a011c 100644
--- a/packages/native_crypto/example/ios/Runner/Info.plist
+++ b/packages/native_crypto/example/ios/Runner/Info.plist
@@ -45,5 +45,11 @@
CADisableMinimumFrameDurationOnPhone
+ UIApplicationSupportsIndirectInputEvents
+
+ UISupportsDocumentBrowser
+
+ LSSupportsOpeningDocumentsInPlace
+
diff --git a/packages/native_crypto/example/lib/core/get_it.dart b/packages/native_crypto/example/lib/core/get_it.dart
new file mode 100644
index 0000000..1de732b
--- /dev/null
+++ b/packages/native_crypto/example/lib/core/get_it.dart
@@ -0,0 +1,35 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:get_it/get_it.dart';
+import 'package:native_crypto_example/data/data_sources/logger_data_source_impl.dart';
+import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart';
+import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart';
+import 'package:native_crypto_example/data/data_sources/session_data_source_impl.dart';
+import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart';
+import 'package:native_crypto_example/domain/data_sources/session_data_source.dart';
+
+final getIt = GetIt.I;
+
+abstract class GetItInitializer {
+ static Future init() async {
+ getIt
+ ..registerLazySingleton(
+ SessionDataSourceImpl.new,
+ )
+ ..registerLazySingleton(
+ LoggerDataSourceImpl.new,
+ )
+ ..registerLazySingleton(
+ NativeCryptoDataSourceImpl.new,
+ )
+ ..registerLazySingleton(
+ PointyCastleDataSourceImpl.new,
+ );
+
+ await getIt.allReady();
+ }
+}
diff --git a/packages/native_crypto/example/lib/core/typography.dart b/packages/native_crypto/example/lib/core/typography.dart
new file mode 100644
index 0000000..3e55d46
--- /dev/null
+++ b/packages/native_crypto/example/lib/core/typography.dart
@@ -0,0 +1,18 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+
+abstract class AppTypography {
+ static const title = TextStyle(
+ color: Colors.black,
+ fontSize: 24,
+ );
+
+ static const body = TextStyle(
+ fontSize: 16,
+ );
+}
diff --git a/packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart
new file mode 100644
index 0000000..022d9e6
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart
@@ -0,0 +1,34 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+
+class LoggerDataSourceImpl extends LoggerDataSource {
+ final Map _logs = {};
+ final StreamController> _streamController =
+ StreamController.broadcast();
+
+ @override
+ Future addLog(LogMessage message) async {
+ _logs[DateTime.now()] = message;
+ _streamController.add(Map.from(_logs));
+ }
+
+ @override
+ Future clearLog() async {
+ _logs.clear();
+ _streamController.add(Map.from(_logs));
+ }
+
+ @override
+ Future> getLogs() async => _logs;
+
+ @override
+ Stream> streamLogs() => _streamController.stream;
+}
diff --git a/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart
new file mode 100644
index 0000000..dd61735
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart
@@ -0,0 +1,150 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
+
+class NativeCryptoDataSourceImpl extends CryptoDataSource {
+ @override
+ Future decrypt(Uint8List data, SecretKey key) async {
+ final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+ );
+ final Uint8List plainText = await cipher.decrypt(
+ CipherText(
+ data,
+ chunkFactory: (bytes) => AESCipherChunk(
+ bytes,
+ ivLength: cipher.mode.ivLength,
+ tagLength: cipher.mode.tagLength,
+ ),
+ ),
+ );
+
+ return plainText;
+ }
+
+ @override
+ Future decryptFile(
+ File cipherText,
+ Uri folderResult,
+ SecretKey key,
+ ) async {
+ final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+ );
+
+ final plainText = File.fromUri(
+ Uri.parse(
+ '${folderResult.path}/${cipherText.path.split('/').last.replaceAll('.enc', '')}',
+ ),
+ );
+ await cipher.decryptFile(
+ cipherText,
+ plainText,
+ );
+ }
+
+ @override
+ Future deriveKeyFromPassword(
+ String password, {
+ required String salt,
+ }) async {
+ final Pbkdf2 pbkdf2 = Pbkdf2(
+ length: 32,
+ iterations: 1000,
+ salt: salt.toBytes(),
+ hashAlgorithm: HashAlgorithm.sha256,
+ );
+ return pbkdf2(password: password);
+ }
+
+ @override
+ Future hash(Hash hasher, Uint8List data) async {
+ final Uint8List digestMessage = await hasher.digest(data);
+
+ return digestMessage;
+ }
+
+ @override
+ Future hmac(Hmac hmac, Uint8List data, SecretKey key) async {
+ final Uint8List digestMessage = await hmac.digest(data, key);
+
+ return digestMessage;
+ }
+
+ @override
+ Future encrypt(Uint8List data, SecretKey key) async {
+ final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+ );
+ final CipherText cipherText = await cipher.encrypt(data);
+
+ return cipherText.bytes;
+ }
+
+ @override
+ Future encryptWithIV(
+ Uint8List data,
+ SecretKey key,
+ Uint8List iv,
+ ) async {
+ final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+ );
+
+ final AESCipherChunk chunk = await cipher.encryptWithIV(data, iv);
+ final CipherText cipherText = CipherText.fromChunks(
+ [chunk],
+ chunkFactory: (bytes) => AESCipherChunk(
+ bytes,
+ ivLength: cipher.mode.ivLength,
+ tagLength: cipher.mode.tagLength,
+ ),
+ );
+
+ return cipherText.bytes;
+ }
+
+ @override
+ Future encryptFile(
+ File plainText,
+ Uri folderResult,
+ SecretKey key,
+ ) async {
+ final AES cipher = AES(
+ key: key,
+ mode: AESMode.gcm,
+ padding: AESPadding.none,
+ );
+
+ final cipherText = File.fromUri(
+ Uri.parse(
+ '${folderResult.path}/${plainText.path.split('/').last}.enc',
+ ),
+ );
+
+ await cipher.encryptFile(plainText, cipherText);
+ }
+
+ @override
+ Future generateSecureRandom(int length) async {
+ final SecretKey sk = await SecretKey.fromSecureRandom(length);
+
+ return sk;
+ }
+}
diff --git a/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart
new file mode 100644
index 0000000..e14d14f
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart
@@ -0,0 +1,183 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+// ignore_for_file: implementation_imports
+
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
+import 'package:pointycastle/export.dart';
+import 'package:pointycastle/src/platform_check/platform_check.dart';
+
+class PointyCastleDataSourceImpl extends CryptoDataSource {
+ FortunaRandom? _secureRandom;
+
+ @override
+ Future decrypt(Uint8List data, SecretKey key) async {
+ final iv = Uint8List.fromList(data.sublist(0, 12));
+ final cipherTextWithoutIv = Uint8List.fromList(
+ data.sublist(12),
+ );
+
+ final gcm = GCMBlockCipher(AESEngine())
+ ..init(
+ false,
+ AEADParameters(
+ KeyParameter(key.bytes),
+ 16 * 8,
+ iv,
+ Uint8List(0),
+ ),
+ );
+ final paddedPlainText = gcm.process(cipherTextWithoutIv);
+
+ return paddedPlainText;
+ }
+
+ @override
+ Future decryptFile(
+ File cipherText,
+ Uri folderResult,
+ SecretKey key,
+ ) async {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future deriveKeyFromPassword(
+ String password, {
+ required String salt,
+ }) async {
+ final derivator = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64))
+ ..init(
+ Pbkdf2Parameters(salt.toBytes(), 1000, 32),
+ );
+
+ final Uint8List sk = derivator.process(password.toBytes());
+
+ return SecretKey(sk);
+ }
+
+ @override
+ Future hash(Hash hasher, Uint8List data) async {
+ final Digest? digest;
+
+ switch (hasher.runtimeType) {
+ case Sha256:
+ digest = SHA256Digest();
+ break;
+ case Sha384:
+ digest = SHA384Digest();
+ break;
+ case Sha512:
+ digest = SHA512Digest();
+ break;
+ default:
+ throw UnsupportedError(
+ 'Unsupported hash algorithm: ${hasher.runtimeType}',
+ );
+ }
+
+ return digest.process(data);
+ }
+
+ @override
+ Future hmac(Hmac hmac, Uint8List data, SecretKey key) async {
+ final HMac? digest;
+
+ switch (hmac.runtimeType) {
+ case HmacSha256:
+ digest = HMac.withDigest(SHA256Digest());
+ digest.init(KeyParameter(key.bytes));
+ break;
+ case Sha384:
+ digest = HMac.withDigest(SHA384Digest());
+ digest.init(KeyParameter(key.bytes));
+ break;
+ case Sha512:
+ digest = HMac.withDigest(SHA512Digest());
+ digest.init(KeyParameter(key.bytes));
+ break;
+ default:
+ throw UnsupportedError(
+ 'Unsupported hmac algorithm: ${hmac.runtimeType}',
+ );
+ }
+
+ return digest.process(data);
+ }
+
+ @override
+ Future encrypt(Uint8List data, SecretKey key) async {
+ final iv = (await generateSecureRandom(12 * 8)).bytes;
+
+ final gcm = GCMBlockCipher(AESEngine())
+ ..init(
+ true,
+ AEADParameters(
+ KeyParameter(key.bytes),
+ 16 * 8,
+ iv,
+ Uint8List(0),
+ ),
+ );
+
+ final cipherText = gcm.process(data);
+
+ return Uint8List.fromList(
+ iv + cipherText,
+ );
+ }
+
+ @override
+ Future encryptWithIV(
+ Uint8List data,
+ SecretKey key,
+ Uint8List iv,
+ ) async {
+ final gcm = GCMBlockCipher(AESEngine())
+ ..init(
+ true,
+ AEADParameters(
+ KeyParameter(key.bytes),
+ 16 * 8,
+ iv,
+ Uint8List(0),
+ ),
+ );
+
+ final cipherText = gcm.process(data);
+
+ return Uint8List.fromList(
+ iv + cipherText,
+ );
+ }
+
+ @override
+ Future encryptFile(
+ File plainText,
+ Uri folderResult,
+ SecretKey key,
+ ) async {
+ throw UnimplementedError();
+ }
+
+ @override
+ Future generateSecureRandom(int length) async {
+ if (_secureRandom == null) {
+ _secureRandom = FortunaRandom();
+ _secureRandom!.seed(
+ KeyParameter(Platform.instance.platformEntropySource().getBytes(32)),
+ );
+ }
+
+ final sk = _secureRandom!.nextBytes(length);
+
+ return SecretKey(sk);
+ }
+}
diff --git a/packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart
new file mode 100644
index 0000000..d7ee01e
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart
@@ -0,0 +1,45 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/data_sources/session_data_source.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+
+class SessionDataSourceImpl extends SessionDataSource {
+ SecretKey? _sk;
+ Mode? _mode = const NativeCryptoMode();
+
+ @override
+ Future getSessionKey() async {
+ if (_sk == null) {
+ throw Exception('Session key is not ready');
+ }
+
+ return _sk!;
+ }
+
+ @override
+ Future isSessionKeyReady() async => _sk != null;
+
+ @override
+ Future setSessionKey(SecretKey key) async {
+ _sk = key;
+ }
+
+ @override
+ Future getCurrentMode() async {
+ if (_mode == null) {
+ throw Exception('Mode is not set');
+ }
+
+ return _mode!;
+ }
+
+ @override
+ Future setCurrentMode(Mode mode) async {
+ _mode = mode;
+ }
+}
diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart
new file mode 100644
index 0000000..2781fb3
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart
@@ -0,0 +1,147 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+import 'package:wyatt_type_utils/wyatt_type_utils.dart';
+
+class CryptoRepositoryImpl extends CryptoRepository {
+ CryptoRepositoryImpl({
+ required this.cryptoDataSource,
+ });
+ CryptoDataSource cryptoDataSource;
+
+ @override
+ FutureOrResult decrypt(Uint8List data, SecretKey key) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.decrypt(data, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult decryptFile(
+ File cipherText,
+ Uri folderResult,
+ SecretKey key,
+ ) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult deriveKeyFromPassword(
+ String password, {
+ required String salt,
+ }) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.deriveKeyFromPassword(
+ password,
+ salt: salt,
+ ),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult hash(Hash hasher, Uint8List data) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.hash(hasher, data),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.hmac(hmac, data, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult encrypt(Uint8List data, SecretKey key) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.encrypt(data, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult encryptWithIV(
+ Uint8List data,
+ SecretKey key,
+ Uint8List iv,
+ ) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.encryptWithIV(data, key, iv),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult encryptFile(
+ File plainText,
+ Uri folderResult,
+ SecretKey key,
+ ) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.encryptFile(plainText, folderResult, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult generateSecureRandom(int length) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.generateSecureRandom(length),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+}
diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart
new file mode 100644
index 0000000..972ac7b
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart
@@ -0,0 +1,167 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+import 'package:wyatt_type_utils/wyatt_type_utils.dart';
+
+class CryptoRepositorySwitchableImpl extends CryptoRepository {
+ CryptoRepositorySwitchableImpl({
+ required this.nativeCryptoDataSource,
+ required this.pointyCastleDataSource,
+ required this.currentMode,
+ });
+
+ CryptoDataSource nativeCryptoDataSource;
+ CryptoDataSource pointyCastleDataSource;
+ Mode currentMode;
+
+ set mode(Mode mode) {
+ currentMode = mode;
+ }
+
+ CryptoDataSource get cryptoDataSource {
+ if (currentMode is NativeCryptoMode) {
+ return nativeCryptoDataSource;
+ } else if (currentMode is PointyCastleMode) {
+ return pointyCastleDataSource;
+ } else {
+ throw Exception('Unknown mode');
+ }
+ }
+
+ @override
+ FutureOrResult decrypt(Uint8List data, SecretKey key) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.decrypt(data, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult decryptFile(
+ File cipherText,
+ Uri folderResult,
+ SecretKey key,
+ ) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.decryptFile(cipherText, folderResult, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult deriveKeyFromPassword(
+ String password, {
+ required String salt,
+ }) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.deriveKeyFromPassword(
+ password,
+ salt: salt,
+ ),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult hash(Hash hasher, Uint8List data) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.hash(hasher, data),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.hmac(hmac, data, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult encrypt(Uint8List data, SecretKey key) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.encrypt(data, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult encryptWithIV(
+ Uint8List data,
+ SecretKey key,
+ Uint8List iv,
+ ) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.encryptWithIV(data, key, iv),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult encryptFile(
+ File plainText,
+ Uri folderResult,
+ SecretKey key,
+ ) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.encryptFile(plainText, folderResult, key),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+
+ @override
+ FutureOrResult generateSecureRandom(int length) =>
+ Result.tryCatchAsync(
+ () async => cryptoDataSource.generateSecureRandom(length),
+ (error) {
+ if (error is NativeCryptoException) {
+ return ClientException('${error.message}');
+ }
+ return ClientException(error.toString());
+ },
+ );
+}
diff --git a/packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart
new file mode 100644
index 0000000..54ba9be
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart
@@ -0,0 +1,41 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+import 'package:wyatt_type_utils/wyatt_type_utils.dart';
+
+class LoggerRepositoryImpl extends LoggerRepository {
+ LoggerRepositoryImpl({
+ required this.loggerDataSource,
+ });
+
+ final LoggerDataSource loggerDataSource;
+
+ @override
+ FutureOrResult addLog(LogMessage message) => Result.tryCatchAsync(
+ () async => loggerDataSource.addLog(message),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ FutureOrResult clearLog() => Result.tryCatchAsync(
+ () async => loggerDataSource.clearLog(),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ FutureOrResult> getLogs() => Result.tryCatchAsync(
+ () async => loggerDataSource.getLogs(),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ StreamResult> streamLogs() =>
+ loggerDataSource.streamLogs().map(Ok.new);
+}
diff --git a/packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart
new file mode 100644
index 0000000..5d2d592
--- /dev/null
+++ b/packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart
@@ -0,0 +1,49 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/data_sources/session_data_source.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+import 'package:wyatt_type_utils/wyatt_type_utils.dart';
+
+class SessionRepositoryImpl extends SessionRepository {
+ SessionRepositoryImpl({
+ required this.sessionDataSource,
+ });
+ SessionDataSource sessionDataSource;
+
+ @override
+ FutureOrResult getSessionKey() => Result.tryCatchAsync(
+ () async => sessionDataSource.getSessionKey(),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ FutureOrResult isSessionKeyReady() => Result.tryCatchAsync(
+ () async => sessionDataSource.isSessionKeyReady(),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ FutureOrResult setSessionKey(SecretKey key) => Result.tryCatchAsync(
+ () async => sessionDataSource.setSessionKey(key),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ FutureOrResult getCurrentMode() => Result.tryCatchAsync(
+ () async => sessionDataSource.getCurrentMode(),
+ (error) => ClientException(error.toString()),
+ );
+
+ @override
+ FutureOrResult setCurrentMode(Mode mode) => Result.tryCatchAsync(
+ () async => sessionDataSource.setCurrentMode(mode),
+ (error) => ClientException(error.toString()),
+ );
+}
diff --git a/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart
new file mode 100644
index 0000000..df7f304
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart
@@ -0,0 +1,38 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+abstract class CryptoDataSource extends BaseDataSource {
+ Future generateSecureRandom(int length);
+ Future deriveKeyFromPassword(
+ String password, {
+ required String salt,
+ });
+ Future encrypt(Uint8List data, SecretKey key);
+ Future encryptFile(
+ File plainText,
+ Uri folderResult,
+ SecretKey key,
+ );
+ Future encryptWithIV(
+ Uint8List data,
+ SecretKey key,
+ Uint8List iv,
+ );
+ Future decrypt(Uint8List data, SecretKey key);
+ Future decryptFile(
+ File cipherText,
+ Uri folderResult,
+ SecretKey key,
+ );
+ Future hash(Hash hasher, Uint8List data);
+ Future hmac(Hmac hmac, Uint8List data, SecretKey key);
+}
diff --git a/packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart
new file mode 100644
index 0000000..fbff97a
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart
@@ -0,0 +1,15 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+abstract class LoggerDataSource extends BaseDataSource {
+ Future addLog(LogMessage message);
+ Future clearLog();
+ Future> getLogs();
+ Stream> streamLogs();
+}
diff --git a/packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart
new file mode 100644
index 0000000..8a31feb
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart
@@ -0,0 +1,17 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+abstract class SessionDataSource extends BaseDataSource {
+ Future setSessionKey(SecretKey key);
+ Future isSessionKeyReady();
+ Future getSessionKey();
+ Future getCurrentMode();
+ Future setCurrentMode(Mode mode);
+}
diff --git a/packages/native_crypto/example/lib/domain/entities/log_message.dart b/packages/native_crypto/example/lib/domain/entities/log_message.dart
new file mode 100644
index 0000000..05edf62
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/entities/log_message.dart
@@ -0,0 +1,27 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+
+class LogMessage {
+ const LogMessage(this.prefix, this.color, this.message);
+
+ final String prefix;
+ final Color color;
+ final String message;
+}
+
+class LogInfo extends LogMessage {
+ const LogInfo(String message) : super('info', Colors.black, message);
+}
+
+class LogWarning extends LogMessage {
+ const LogWarning(String message) : super('warn', Colors.orange, message);
+}
+
+class LogError extends LogMessage {
+ const LogError(String message) : super('fail', Colors.red, message);
+}
diff --git a/packages/native_crypto/example/lib/domain/entities/mode.dart b/packages/native_crypto/example/lib/domain/entities/mode.dart
new file mode 100644
index 0000000..d35e901
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/entities/mode.dart
@@ -0,0 +1,34 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+class Mode extends Entity {
+ const Mode(
+ this.primaryColor,
+ this.secondaryColor,
+ );
+
+ final Color primaryColor;
+ final Color secondaryColor;
+}
+
+class NativeCryptoMode extends Mode {
+ const NativeCryptoMode()
+ : super(
+ Colors.blue,
+ Colors.black,
+ );
+}
+
+class PointyCastleMode extends Mode {
+ const PointyCastleMode()
+ : super(
+ Colors.red,
+ Colors.white,
+ );
+}
diff --git a/packages/native_crypto/example/lib/domain/entities/states.dart b/packages/native_crypto/example/lib/domain/entities/states.dart
new file mode 100644
index 0000000..0e1482c
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/entities/states.dart
@@ -0,0 +1,7 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+enum State { initial, loading, success, failure }
diff --git a/packages/native_crypto/example/lib/domain/entities/test_vector.dart b/packages/native_crypto/example/lib/domain/entities/test_vector.dart
new file mode 100644
index 0000000..5682213
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/entities/test_vector.dart
@@ -0,0 +1,54 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/foundation.dart';
+import 'package:native_crypto/native_crypto.dart';
+
+class TestVector {
+ TestVector({
+ required this.key,
+ required this.nonce,
+ required this.plainText,
+ required this.cipherText,
+ required this.tag,
+ });
+
+ final String key;
+ final String nonce;
+ final String plainText;
+ final String cipherText;
+ final String tag;
+
+ Uint8List get keyBytes => key.toBytes(from: Encoding.base16);
+ Uint8List get nonceBytes => nonce.toBytes(from: Encoding.base16);
+ Uint8List get tagBytes => tag.toBytes(from: Encoding.base16);
+ Uint8List get plainTextBytes => plainText.toBytes(from: Encoding.base16);
+ Uint8List get cipherTextBytes {
+ final iv = nonceBytes;
+ final data = cipherText.toBytes(from: Encoding.base16);
+ final tag = tagBytes;
+ return Uint8List.fromList(iv + data + tag);
+ }
+
+ bool validateCipherText(Uint8List? testCipherText) {
+ final result = listEquals(testCipherText, cipherTextBytes);
+ if (!result) {
+ debugPrint('Cipher texts differs:\n$cipherTextBytes\n$testCipherText');
+ }
+ return result;
+ }
+
+ bool validatePlainText(Uint8List? testPlainText) {
+ final result = listEquals(testPlainText, plainTextBytes);
+ if (!result) {
+ debugPrint('Plain texts differs:\n$plainTextBytes\n$testPlainText');
+ }
+ return result;
+ }
+
+ bool validate(Uint8List? testPlainText, Uint8List? testCipherText) =>
+ validateCipherText(testCipherText) && validatePlainText(testPlainText);
+}
diff --git a/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart
new file mode 100644
index 0000000..6045be6
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart
@@ -0,0 +1,39 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+abstract class CryptoRepository extends BaseRepository {
+ FutureOrResult generateSecureRandom(int length);
+ FutureOrResult deriveKeyFromPassword(
+ String password, {
+ required String salt,
+ });
+ FutureOrResult encrypt(Uint8List data, SecretKey key);
+ FutureOrResult encryptFile(
+ File plainText,
+ Uri folderResult,
+ SecretKey key,
+ );
+ FutureOrResult encryptWithIV(
+ Uint8List data,
+ SecretKey key,
+ Uint8List iv,
+ );
+ FutureOrResult decrypt(Uint8List data, SecretKey key);
+ FutureOrResult decryptFile(
+ File cipherText,
+ Uri folderResult,
+ SecretKey key,
+ );
+
+ FutureOrResult hash(Hash hasher, Uint8List data);
+ FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key);
+}
diff --git a/packages/native_crypto/example/lib/domain/repositories/logger_repository.dart b/packages/native_crypto/example/lib/domain/repositories/logger_repository.dart
new file mode 100644
index 0000000..ed29af4
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/repositories/logger_repository.dart
@@ -0,0 +1,18 @@
+// Author: Hugo Pointcheval
+// Email: git@pcl.ovh
+// -----
+// File: logger_repository.dart
+// Created Date: 09/01/2023 22:12:56
+// Last Modified: 09/01/2023 23:03:51
+// -----
+// Copyright (c) 2023
+
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+abstract class LoggerRepository extends BaseRepository {
+ FutureOrResult addLog(LogMessage message);
+ FutureOrResult clearLog();
+ FutureOrResult> getLogs();
+ StreamResult> streamLogs();
+}
diff --git a/packages/native_crypto/example/lib/domain/repositories/session_repository.dart b/packages/native_crypto/example/lib/domain/repositories/session_repository.dart
new file mode 100644
index 0000000..2ecce6f
--- /dev/null
+++ b/packages/native_crypto/example/lib/domain/repositories/session_repository.dart
@@ -0,0 +1,20 @@
+// Author: Hugo Pointcheval
+// Email: git@pcl.ovh
+// -----
+// File: session_repository.dart
+// Created Date: 08/01/2023 17:35:46
+// Last Modified: 09/01/2023 22:13:21
+// -----
+// Copyright (c) 2023
+
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+
+abstract class SessionRepository extends BaseRepository {
+ FutureOrResult setSessionKey(SecretKey key);
+ FutureOrResult isSessionKeyReady();
+ FutureOrResult getSessionKey();
+ FutureOrResult getCurrentMode();
+ FutureOrResult setCurrentMode(Mode mode);
+}
diff --git a/packages/native_crypto/example/lib/home.dart b/packages/native_crypto/example/lib/home.dart
deleted file mode 100644
index 8357b7e..0000000
--- a/packages/native_crypto/example/lib/home.dart
+++ /dev/null
@@ -1,64 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: home.dart
-// Created Date: 28/12/2021 13:48:36
-// Last Modified: 28/12/2021 15:18:03
-// -----
-// Copyright (c) 2021
-
-import 'package:flutter/material.dart';
-import 'package:native_crypto_example/pages/benchmark_page.dart';
-
-import 'pages/cipher_page.dart';
-import 'pages/kdf_page.dart';
-
-class Home extends StatefulWidget {
- const Home({Key? key}) : super(key: key);
-
- @override
- _HomeState createState() => _HomeState();
-}
-
-class _HomeState extends State {
- int _currentIndex = 0;
- final List _children = [KdfPage(), CipherPage(), BenchmarkPage()];
-
- void onTabTapped(int index) {
- setState(() {
- _currentIndex = index;
- });
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- centerTitle: true,
- title: const Text('Native Crypto'),
- ),
- body: _children[_currentIndex],
- bottomNavigationBar: BottomNavigationBar(
- selectedItemColor: Colors.blue,
- unselectedItemColor: Colors.black,
- showUnselectedLabels: true,
- onTap: onTabTapped, // new
- currentIndex: _currentIndex, // new
- items: const [
- BottomNavigationBarItem(
- icon: Icon(Icons.vpn_key),
- label: 'Key',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.lock),
- label: 'Encryption',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.timer),
- label: 'Benchmark',
- ),
- ],
- ),
- );
- }
-}
diff --git a/packages/native_crypto/example/lib/main.dart b/packages/native_crypto/example/lib/main.dart
index 65b60e0..07da36e 100644
--- a/packages/native_crypto/example/lib/main.dart
+++ b/packages/native_crypto/example/lib/main.dart
@@ -3,23 +3,16 @@
// -----
// File: main.dart
// Created Date: 27/12/2021 21:15:12
-// Last Modified: 28/12/2021 13:51:36
+// Last Modified: 10/01/2023 14:59:05
// -----
// Copyright (c) 2021
import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:native_crypto_example/home.dart';
+import 'package:native_crypto_example/core/get_it.dart';
+import 'package:native_crypto_example/presentation/app/app.dart';
-void main() {
- runApp(const ProviderScope(child: MyApp()));
-}
-
-class MyApp extends StatelessWidget {
- const MyApp({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return const MaterialApp(home: Home());
- }
+Future main() async {
+ await GetItInitializer.init();
+
+ runApp(App());
}
diff --git a/packages/native_crypto/example/lib/pages/benchmark_page.dart b/packages/native_crypto/example/lib/pages/benchmark_page.dart
deleted file mode 100644
index 6396152..0000000
--- a/packages/native_crypto/example/lib/pages/benchmark_page.dart
+++ /dev/null
@@ -1,189 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: benchmark_page.dart
-// Created Date: 28/12/2021 15:12:39
-// Last Modified: 26/05/2022 20:19:28
-// -----
-// Copyright (c) 2021
-
-import 'dart:math';
-import 'dart:typed_data';
-
-import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:native_crypto/native_crypto.dart';
-import 'package:native_crypto_example/pointycastle/aes_gcm.dart';
-import 'package:native_crypto_example/widgets/button.dart';
-
-import '../session.dart';
-import '../widgets/output.dart';
-
-class BenchmarkPage extends ConsumerWidget {
- BenchmarkPage({Key? key}) : super(key: key);
-
- final Output keyContent = Output();
- final Output benchmarkStatus = Output(large: true);
-
- Future _benchmark(
- WidgetRef ref,
- Cipher cipher, {
- bool usePc = false,
- bool encryptionOnly = false,
- }) async {
- Session state = ref.read(sessionProvider.state).state;
- AesGcm pc = AesGcm();
-
- if (state.secretKey.bytes.isEmpty) {
- benchmarkStatus
- .print('No SecretKey!\nGo in Key tab and generate or derive one.');
- return;
- }
-
- List testedSizes = [2, 4, 8, 16, 32, 64, 128, 256];
- int multiplier = pow(2, 20).toInt(); // MiB
-
- benchmarkStatus.print("[Benchmark] Sizes: ${testedSizes.join('/')}MiB\n");
- benchmarkStatus.appendln(
- "[Benchmark] Engine: " + (usePc ? " PointyCastle" : " NativeCrypto"));
- benchmarkStatus.appendln("[Benchmark] Test: " +
- (encryptionOnly ? " Encryption Only" : " Encryption & Decryption"));
- benchmarkStatus.appendln(
- '[Benchmark] bytesCountPerChunk: ${Cipher.bytesCountPerChunk} bytes/chunk');
-
- String csv = encryptionOnly
- ? "Run;Size (B);Encryption Time (ms)\n"
- : "Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n";
-
- int run = 0;
- var beforeBench = DateTime.now();
-
- for (int size in testedSizes) {
- run++;
- final StringBuffer csvLine = StringBuffer();
- final dummyBytes = Uint8List(size * multiplier);
- csvLine.write('$run;${size * multiplier};');
-
- // Encryption
- Object encryptedBigFile;
- var before = DateTime.now();
- if (usePc) {
- encryptedBigFile = pc.encrypt(dummyBytes, state.secretKey.bytes);
- } else {
- encryptedBigFile = await cipher.encrypt(dummyBytes);
- }
- var after = DateTime.now();
-
- var benchmark =
- after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
- benchmarkStatus
- .appendln('[Benchmark] ${size}MiB => Encryption took $benchmark ms');
- csvLine.write('$benchmark');
-
- if (!encryptionOnly) {
- // Decryption
- before = DateTime.now();
- if (usePc) {
- pc.decrypt(encryptedBigFile as Uint8List, state.secretKey.bytes);
- } else {
- await cipher.decrypt(encryptedBigFile as CipherTextWrapper);
- }
- after = DateTime.now();
- benchmark =
- after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
- benchmarkStatus.appendln(
- '[Benchmark] ${size}MiB => Decryption took $benchmark ms');
- csvLine.write(';$benchmark');
- }
- csv += csvLine.toString() + '\n';
- }
- var afterBench = DateTime.now();
- var benchmark =
- afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch;
- var sum = testedSizes.reduce((a, b) => a + b);
- benchmarkStatus
- .appendln('[Benchmark] Finished: ${sum}MiB in $benchmark ms');
- benchmarkStatus.appendln('[Benchmark] Check the console for csv data');
- benchmarkStatus.appendln(csv);
- debugPrint(csv);
- }
-
- void _clear() {
- benchmarkStatus.clear();
- }
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- Session state = ref.read(sessionProvider.state).state;
- if (state.secretKey.bytes.isEmpty) {
- keyContent
- .print('No SecretKey!\nGo in Key tab and generate or derive one.');
- return SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Column(
- children: [
- const Align(
- child: Text("Secret Key"),
- alignment: Alignment.centerLeft,
- ),
- keyContent,
- ],
- ),
- ),
- );
- }
- keyContent.print(state.secretKey.bytes.toString());
-
- AES cipher = AES(state.secretKey);
-
- return SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Column(
- children: [
- const Align(
- child: Text("Secret Key"),
- alignment: Alignment.centerLeft,
- ),
- keyContent,
- Wrap(
- children: [
- Button(
- () => _benchmark(ref, cipher),
- "NativeCrypto",
- ),
- const SizedBox(width: 8),
- Button(
- () => _benchmark(ref, cipher, usePc: true),
- "PointyCastle",
- ),
- const SizedBox(width: 8),
- Button(
- () => _benchmark(ref, cipher, encryptionOnly: true),
- "NC Encryption Only",
- ),
- const SizedBox(width: 8),
- Button(
- () => _benchmark(
- ref,
- cipher,
- usePc: true,
- encryptionOnly: true,
- ),
- "PC Encryption Only",
- ),
- const SizedBox(width: 8),
- Button(
- _clear,
- "Clear",
- ),
- ],
- ),
- benchmarkStatus,
- ],
- ),
- ),
- );
- }
-}
diff --git a/packages/native_crypto/example/lib/pages/cipher_page.dart b/packages/native_crypto/example/lib/pages/cipher_page.dart
deleted file mode 100644
index b0843c7..0000000
--- a/packages/native_crypto/example/lib/pages/cipher_page.dart
+++ /dev/null
@@ -1,157 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: cipher_page.dart
-// Created Date: 28/12/2021 13:33:15
-// Last Modified: 27/05/2022 16:42:10
-// -----
-// Copyright (c) 2021
-
-import 'dart:typed_data';
-
-import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:native_crypto/native_crypto.dart';
-import 'package:native_crypto/native_crypto_ext.dart';
-import 'package:native_crypto_example/widgets/button.dart';
-
-import '../session.dart';
-import '../widgets/output.dart';
-
-// ignore: must_be_immutable
-class CipherPage extends ConsumerWidget {
- CipherPage({Key? key}) : super(key: key);
-
- final Output keyContent = Output();
- final Output encryptionStatus = Output();
- final Output decryptionStatus = Output();
-
- final TextEditingController _plainTextController = TextEditingController()
- ..text = 'PlainText';
- CipherTextWrapper? cipherText;
-
- Future _encrypt(WidgetRef ref, Cipher cipher) async {
- Session state = ref.read(sessionProvider.state).state;
- final plainText = _plainTextController.text.trim();
-
- if (state.secretKey.bytes.isEmpty) {
- encryptionStatus
- .print('No SecretKey!\nGo in Key tab and generate or derive one.');
- } else if (plainText.isEmpty) {
- encryptionStatus.print('Entry is empty');
- } else {
- var stringToBytes = plainText.toBytes();
- cipherText = await cipher.encrypt(stringToBytes);
- encryptionStatus.print('String successfully encrypted:\n');
-
- CipherText unwrap = cipherText!.unwrap();
- encryptionStatus.append(unwrap.base16);
- }
- }
-
- Future _alter() async {
- if (cipherText == null) {
- decryptionStatus.print('Encrypt before altering CipherText!');
- } else {
- // Add 1 to the first byte
- Uint8List _altered = cipherText!.unwrap().bytes;
- _altered[0] += 1;
- // Recreate cipher text with altered data
- cipherText = CipherTextWrapper.fromBytes(
- _altered,
- ivLength: AESMode.gcm.ivLength,
- tagLength: AESMode.gcm.tagLength,
- );
- encryptionStatus.print('String successfully encrypted:\n');
-
- CipherText unwrap = cipherText!.unwrap();
- encryptionStatus.appendln(unwrap.base16);
- decryptionStatus.print('CipherText altered!\nDecryption will fail.');
- }
- }
-
- void _decrypt(WidgetRef ref, Cipher cipher) async {
- Session state = ref.read(sessionProvider.state).state;
-
- if (state.secretKey.bytes.isEmpty) {
- decryptionStatus
- .print('No SecretKey!\nGo in Key tab and generate or derive one.');
- } else if (cipherText == null) {
- decryptionStatus.print('Encrypt before decrypting!');
- } else {
- try {
- Uint8List plainText = await cipher.decrypt(cipherText!);
- var bytesToString = plainText.toStr();
- decryptionStatus
- .print('String successfully decrypted:\n\n$bytesToString');
- } on NativeCryptoException catch (e) {
- decryptionStatus.print(e.message ?? 'Decryption failed!');
- }
- }
- }
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- Session state = ref.read(sessionProvider.state).state;
- if (state.secretKey.bytes.isEmpty) {
- keyContent
- .print('No SecretKey!\nGo in Key tab and generate or derive one.');
- return SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Column(
- children: [
- const Align(
- child: Text("Secret Key"),
- alignment: Alignment.centerLeft,
- ),
- keyContent,
- ],
- ),
- ),
- );
- }
- keyContent.print(state.secretKey.bytes.toString());
-
- AES cipher = AES(state.secretKey);
- return SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Column(
- children: [
- const Align(
- child: Text("Secret Key"),
- alignment: Alignment.centerLeft,
- ),
- keyContent,
- TextField(
- controller: _plainTextController,
- decoration: const InputDecoration(
- hintText: 'Plain text',
- ),
- ),
- Button(
- () => _encrypt(ref, cipher),
- "Encrypt",
- ),
- encryptionStatus,
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Button(
- _alter,
- "Alter cipher",
- ),
- Button(
- () => _decrypt(ref, cipher),
- "Decrypt",
- ),
- ],
- ),
- decryptionStatus,
- ],
- ),
- ),
- );
- }
-}
diff --git a/packages/native_crypto/example/lib/pages/kdf_page.dart b/packages/native_crypto/example/lib/pages/kdf_page.dart
deleted file mode 100644
index 371d883..0000000
--- a/packages/native_crypto/example/lib/pages/kdf_page.dart
+++ /dev/null
@@ -1,136 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: kdf_page.dart
-// Created Date: 28/12/2021 13:40:34
-// Last Modified: 26/05/2022 21:09:47
-// -----
-// Copyright (c) 2021
-
-import 'dart:typed_data';
-
-import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:native_crypto/native_crypto.dart';
-import 'package:native_crypto/native_crypto_ext.dart';
-import 'package:native_crypto_example/widgets/button.dart';
-
-import '../session.dart';
-import '../widgets/output.dart';
-
-class KdfPage extends ConsumerWidget {
- KdfPage({Key? key}) : super(key: key);
-
- final Output keyContent = Output();
- final Output keyStatus = Output();
- final Output pbkdf2Status = Output();
- final Output hashStatus = Output(large: true);
-
- final TextEditingController _pwdTextController = TextEditingController()
- ..text = 'Password';
- final TextEditingController _messageTextController = TextEditingController()
- ..text = 'Message';
-
- Future _generate(WidgetRef ref) async {
- Session state = ref.read(sessionProvider.state).state;
- try {
- SecretKey sk = await SecretKey.fromSecureRandom(256);
- state.setKey(sk);
- keyStatus.print(
- "SecretKey successfully generated.\nLength: ${state.secretKey.bytes.length} bytes");
- keyContent.print(state.secretKey.bytes.toString());
- debugPrint("As hex :\n${sk.base16}");
- } catch (e) {
- keyStatus.print(e.toString());
- }
- }
-
- Future _pbkdf2(WidgetRef ref) async {
- Session state = ref.read(sessionProvider.state).state;
- final password = _pwdTextController.text.trim();
-
- if (password.isEmpty) {
- pbkdf2Status.print('Password is empty');
- } else {
- Pbkdf2 _pbkdf2 = Pbkdf2(
- keyBytesCount: 32,
- iterations: 1000,
- algorithm: HashAlgorithm.sha512,
- );
- SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt');
- state.setKey(sk);
- pbkdf2Status.print('Key successfully derived.');
- keyContent.print(state.secretKey.bytes.toString());
- debugPrint("As hex :\n${sk.base16}");
- }
- }
-
- Future _hash(HashAlgorithm hasher) async {
- final message = _messageTextController.text.trim();
- if (message.isEmpty) {
- hashStatus.print('Message is empty');
- } else {
- Uint8List hash = await hasher.digest(message.toBytes());
- hashStatus.print(
- 'Message successfully hashed with $hasher :${hash.toStr(to: Encoding.base16)}');
- }
- }
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- return SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Column(
- children: [
- const Align(
- child: Text("SecretKey"),
- alignment: Alignment.centerLeft,
- ),
- keyContent,
- Button(
- () => _generate(ref),
- "Generate SecretKey",
- ),
- keyStatus,
- TextField(
- controller: _pwdTextController,
- decoration: const InputDecoration(
- hintText: 'Password',
- ),
- ),
- Button(
- () => _pbkdf2(ref),
- "Apply PBKDF2",
- ),
- pbkdf2Status,
- TextField(
- controller: _messageTextController,
- decoration: const InputDecoration(
- hintText: 'Message',
- ),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Button(
- () => _hash(HashAlgorithm.sha256),
- "SHA256",
- ),
- Button(
- () => _hash(HashAlgorithm.sha384),
- "SHA384",
- ),
- Button(
- () => _hash(HashAlgorithm.sha512),
- "SHA512",
- ),
- ],
- ),
- hashStatus,
- ],
- ),
- ),
- );
- }
-}
diff --git a/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart b/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart
index 967c384..67d0483 100644
--- a/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart
+++ b/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart
@@ -1,11 +1,8 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: aes_gcm.dart
-// Created Date: 24/05/2022 16:34:54
-// Last Modified: 27/05/2022 17:36:31
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
// ignore_for_file: implementation_imports
@@ -69,7 +66,8 @@ class AesGcm {
_secureRandom = FortunaRandom();
_secureRandom!.seed(
- KeyParameter(Platform.instance.platformEntropySource().getBytes(32)));
+ KeyParameter(Platform.instance.platformEntropySource().getBytes(32)),
+ );
}
// Use it to generate the random bytes
diff --git a/packages/native_crypto/example/lib/presentation/app/app.dart b/packages/native_crypto/example/lib/presentation/app/app.dart
new file mode 100644
index 0000000..f9253a4
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/app/app.dart
@@ -0,0 +1,65 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto_example/core/get_it.dart';
+import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart';
+import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart';
+import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
+import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart';
+import 'package:native_crypto_example/data/repositories/session_repository_impl.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
+import 'package:native_crypto_example/presentation/home/state_management/home_state_management.dart';
+import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class App extends StatelessWidget {
+ App({super.key});
+
+ final LoggerRepository _loggerRepository =
+ LoggerRepositoryImpl(loggerDataSource: getIt());
+
+ final SessionRepository _sessionRepository =
+ SessionRepositoryImpl(sessionDataSource: getIt());
+
+ final CryptoRepository _cryptoRepository = CryptoRepositorySwitchableImpl(
+ nativeCryptoDataSource: getIt(),
+ pointyCastleDataSource: getIt(),
+ currentMode: const NativeCryptoMode(),
+ );
+
+ @override
+ Widget build(BuildContext context) => MultiProvider(
+ repositoryProviders: [
+ RepositoryProvider.value(value: _loggerRepository),
+ RepositoryProvider.value(
+ value: _sessionRepository,
+ ),
+ RepositoryProvider.value(value: _cryptoRepository),
+ ],
+ blocProviders: [
+ BlocProvider(
+ create: (_) => OutputCubit(_loggerRepository),
+ ),
+ BlocProvider(
+ create: (_) => ModeSwitcherCubit(
+ _sessionRepository,
+ _cryptoRepository,
+ ),
+ )
+ ],
+ child: MaterialApp(
+ title: 'NativeCrypto',
+ debugShowCheckedModeBanner: false,
+ home: HomeStateManagement(),
+ ),
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart
new file mode 100644
index 0000000..3c766fe
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart
@@ -0,0 +1,132 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/entities/states.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+
+part 'benchmark_state.dart';
+
+class BenchmarkCubit extends Cubit {
+ BenchmarkCubit({
+ required this.sessionRepository,
+ required this.loggerRepository,
+ required this.cryptoRepository,
+ }) : super(const BenchmarkState.initial());
+ final SessionRepository sessionRepository;
+ final LoggerRepository loggerRepository;
+ final CryptoRepository cryptoRepository;
+
+ List testedSizes = [
+ 2097152,
+ 6291456,
+ 10485760,
+ 14680064,
+ 18874368,
+ 23068672,
+ 27262976,
+ 31457280,
+ 35651584,
+ 39845888,
+ 44040192,
+ 48234496,
+ 52428800,
+ ];
+
+ FutureOr launchBenchmark() async {
+ emit(const BenchmarkState.loading());
+
+ final sk = await sessionRepository.getSessionKey();
+
+ if (sk.isErr) {
+ await loggerRepository.addLog(
+ const LogError('No SecretKey!\n'
+ 'Go in Key tab and generate or derive one.'),
+ );
+ emit(
+ BenchmarkState.failure(
+ sk.err?.message,
+ ),
+ );
+ }
+
+ int run = 0;
+ final csv = StringBuffer(
+ 'Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n',
+ );
+ for (final size in testedSizes) {
+ run++;
+ final StringBuffer csvLine = StringBuffer();
+ final dummyBytes = Uint8List(size);
+ csvLine.write('$run;$size;');
+
+ // Encryption
+ final beforeEncryption = DateTime.now();
+
+ final encryptedBigFileResult = await cryptoRepository.encrypt(
+ dummyBytes,
+ sk.ok!,
+ );
+
+ final afterEncryption = DateTime.now();
+
+ final benchmarkEncryption = afterEncryption.millisecondsSinceEpoch -
+ beforeEncryption.millisecondsSinceEpoch;
+
+ await loggerRepository.addLog(
+ LogInfo(
+ '[Benchmark] ${size ~/ 1000000}MB => Encryption took $benchmarkEncryption ms',
+ ),
+ );
+
+ csvLine.write('$benchmarkEncryption');
+
+ if (encryptedBigFileResult.isErr) {
+ await loggerRepository.addLog(
+ LogError(
+ 'Encryption failed: ${encryptedBigFileResult.err?.message}',
+ ),
+ );
+ emit(
+ BenchmarkState.failure(
+ encryptedBigFileResult.err?.message,
+ ),
+ );
+ return;
+ }
+
+ // Decryption
+ final beforeDecryption = DateTime.now();
+ await cryptoRepository.decrypt(
+ encryptedBigFileResult.ok!,
+ sk.ok!,
+ );
+ final afterDecryption = DateTime.now();
+ final benchmarkDecryption = afterDecryption.millisecondsSinceEpoch -
+ beforeDecryption.millisecondsSinceEpoch;
+ await loggerRepository.addLog(
+ LogInfo(
+ '[Benchmark] ${size ~/ 1000000}MB => Decryption took $benchmarkDecryption ms',
+ ),
+ );
+
+ csvLine.write(';$benchmarkDecryption');
+ csv.writeln(csvLine);
+ }
+ debugPrint(csv.toString());
+ emit(
+ const BenchmarkState.success(),
+ );
+
+ return;
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart
new file mode 100644
index 0000000..8cf7978
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart
@@ -0,0 +1,27 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'benchmark_cubit.dart';
+
+@immutable
+class BenchmarkState {
+ const BenchmarkState.initial()
+ : state = State.initial,
+ error = null;
+
+ const BenchmarkState.loading()
+ : state = State.loading,
+ error = null;
+
+ const BenchmarkState.failure(this.error) : state = State.failure;
+
+ const BenchmarkState.success()
+ : state = State.success,
+ error = null;
+
+ final State state;
+ final String? error;
+}
diff --git a/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart b/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart
new file mode 100644
index 0000000..dc2627e
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart
@@ -0,0 +1,55 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+import 'package:native_crypto_example/presentation/benchmark/blocs/benchmark_cubit.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
+import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class BenchmarkStateManagement
+ extends CubitScreen {
+ const BenchmarkStateManagement({super.key});
+
+ @override
+ BenchmarkCubit create(BuildContext context) => BenchmarkCubit(
+ sessionRepository: repo(context),
+ loggerRepository: repo(context),
+ cryptoRepository: repo(context),
+ );
+
+ @override
+ Widget onBuild(BuildContext context, BenchmarkState state) => ListView(
+ children: [
+ const Logs(),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'Benchmark',
+ style: AppTypography.title,
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ '''In computer science, a benchmark is a standardized way to measure the performance of a software program or hardware device. A benchmark is typically a set of tests or tasks designed to measure how quickly a program can complete a given set of operations or how efficiently a hardware device can perform a specific task.''',
+ style: AppTypography.body,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Launch',
+ onPressed: () => bloc(context).launchBenchmark(),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart
new file mode 100644
index 0000000..13b3435
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart
@@ -0,0 +1,417 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:file_picker/file_picker.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/entities/states.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+
+part 'aes_state.dart';
+
+class AESCubit extends Cubit {
+ AESCubit({
+ required this.sessionRepository,
+ required this.loggerRepository,
+ required this.cryptoRepository,
+ }) : super(const AESState.initial());
+ final SessionRepository sessionRepository;
+ final LoggerRepository loggerRepository;
+ final CryptoRepository cryptoRepository;
+
+ FutureOr encrypt(String message) async {
+ emit(state.copyWith(state: State.loading));
+
+ final sk = await sessionRepository.getSessionKey();
+
+ if (sk.isErr) {
+ await loggerRepository.addLog(
+ const LogError('No SecretKey!\n'
+ 'Go in Key tab and generate or derive one.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ plainText: message.toBytes(),
+ error: sk.err?.message,
+ ),
+ );
+
+ return;
+ }
+
+ final result = await cryptoRepository.encrypt(message.toBytes(), sk.ok!);
+
+ emit(
+ await result.foldAsync(
+ (cipherText) async {
+ await loggerRepository.addLog(
+ LogInfo('String successfully encrypted.\n'
+ 'Length: ${cipherText.length} bytes.\n'
+ 'Hex:\n${cipherText.toStr(to: Encoding.base16)}'),
+ );
+ return state.copyWith(
+ state: State.success,
+ plainText: message.toBytes(),
+ cipherText: cipherText,
+ plainTextFile: '',
+ cipherTextFile: '',
+ );
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during encryption.'),
+ );
+ return state.copyWith(
+ state: State.failure,
+ plainText: message.toBytes(),
+ error: error.message,
+ );
+ },
+ ),
+ );
+
+ return;
+ }
+
+ FutureOr alterMemory() async {
+ emit(state.copyWith(state: State.loading));
+
+ if (state.cipherText?.isEmpty ?? true) {
+ const error = 'Encrypt before decrypting!';
+ await loggerRepository.addLog(
+ const LogError(error),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: error,
+ ),
+ );
+
+ return;
+ }
+
+ final altered = state.cipherText!;
+ altered[0] += 1;
+
+ await loggerRepository.addLog(
+ const LogWarning('In memory cipher text altered.'),
+ );
+
+ emit(state.copyWith(cipherText: altered));
+
+ return;
+ }
+
+ FutureOr decryptFromMemory() async {
+ emit(state.copyWith(state: State.loading));
+
+ final sk = await sessionRepository.getSessionKey();
+
+ if (sk.isErr) {
+ await loggerRepository.addLog(
+ const LogError('No SecretKey!\n'
+ 'Go in Key tab and generate or derive one.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: sk.err?.message,
+ ),
+ );
+
+ return;
+ }
+
+ if (state.cipherText?.isEmpty ?? true) {
+ const error = 'Encrypt before decrypting!';
+ await loggerRepository.addLog(
+ const LogError(error),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: error,
+ ),
+ );
+
+ return;
+ }
+
+ final result = await cryptoRepository.decrypt(state.cipherText!, sk.ok!);
+
+ emit(
+ await result.foldAsync(
+ (plainText) async {
+ await loggerRepository.addLog(
+ LogInfo('String successfully decrypted.\n'
+ 'Text:\n${plainText.toStr()}'),
+ );
+ return state.copyWith(
+ state: State.success,
+ plainText: plainText,
+ plainTextFile: '',
+ cipherTextFile: '',
+ );
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during decryption.'),
+ );
+ return state.copyWith(
+ state: State.failure,
+ error: error.message,
+ );
+ },
+ ),
+ );
+
+ return;
+ }
+
+ FutureOr decryptFromBase16(String message) async {
+ emit(state.copyWith(state: State.loading));
+
+ final sk = await sessionRepository.getSessionKey();
+ final cipherText = message.toBytes(from: Encoding.base16);
+
+ if (sk.isErr) {
+ await loggerRepository.addLog(
+ const LogError('No SecretKey!\n'
+ 'Go in Key tab and generate or derive one.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ cipherText: cipherText,
+ error: sk.err?.message,
+ ),
+ );
+
+ return;
+ }
+
+ final result = await cryptoRepository.decrypt(cipherText, sk.ok!);
+
+ emit(
+ await result.foldAsync(
+ (plainText) async {
+ await loggerRepository.addLog(
+ LogInfo('String successfully decrypted.\n'
+ 'Text:\n${plainText.toStr()}'),
+ );
+ return state.copyWith(
+ state: State.success,
+ plainText: plainText,
+ cipherText: cipherText,
+ plainTextFile: '',
+ cipherTextFile: '',
+ );
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during decryption.'),
+ );
+ return state.copyWith(
+ state: State.failure,
+ cipherText: cipherText,
+ error: error.message,
+ );
+ },
+ ),
+ );
+
+ return;
+ }
+
+ FutureOr encryptFile() async {
+ emit(state.copyWith(state: State.loading));
+
+ final sk = await sessionRepository.getSessionKey();
+
+ if (sk.isErr) {
+ await loggerRepository.addLog(
+ const LogError('No SecretKey!\n'
+ 'Go in Key tab and generate or derive one.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: sk.err?.message,
+ ),
+ );
+
+ return;
+ }
+
+ // Pick file to encrypt
+ final pickFileResult = await FilePicker.platform.pickFiles();
+
+ if (pickFileResult == null) {
+ await loggerRepository.addLog(
+ const LogError('No file selected.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: 'No file selected.',
+ ),
+ );
+
+ return;
+ }
+
+ final file = File(pickFileResult.files.single.path!);
+
+ // Pick folder to store the encrypted file
+ final resultFolder = await FilePicker.platform.getDirectoryPath();
+
+ if (resultFolder == null) {
+ await loggerRepository.addLog(
+ const LogError('No folder selected.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: 'No folder selected.',
+ ),
+ );
+
+ return;
+ }
+
+ final folder = Directory(resultFolder);
+
+ final encryption = await cryptoRepository.encryptFile(
+ file,
+ folder.uri,
+ sk.ok!,
+ );
+
+ emit(
+ await encryption.foldAsync(
+ (_) async {
+ await loggerRepository.addLog(
+ const LogInfo('File successfully encrypted.\n'),
+ );
+ return state.copyWith(
+ state: State.success,
+ plainTextFile: '',
+ cipherTextFile: '',
+ );
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during encryption.'),
+ );
+ return state.copyWith(
+ state: State.failure,
+ error: error.message,
+ );
+ },
+ ),
+ );
+
+ return;
+ }
+
+ FutureOr decryptFile() async {
+ emit(state.copyWith(state: State.loading));
+
+ final sk = await sessionRepository.getSessionKey();
+
+ if (sk.isErr) {
+ await loggerRepository.addLog(
+ const LogError('No SecretKey!\n'
+ 'Go in Key tab and generate or derive one.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: sk.err?.message,
+ ),
+ );
+
+ return;
+ }
+
+ await FilePicker.platform.clearTemporaryFiles();
+
+ final resultPickFile = await FilePicker.platform.pickFiles();
+
+ if (resultPickFile == null) {
+ await loggerRepository.addLog(
+ const LogError('No file selected.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: 'No file selected.',
+ ),
+ );
+
+ return;
+ }
+
+ final file = File(resultPickFile.files.single.path!);
+
+ // Pick folder to store the encrypted file
+ final resultFolder = await FilePicker.platform.getDirectoryPath();
+
+ if (resultFolder == null) {
+ await loggerRepository.addLog(
+ const LogError('No folder selected.'),
+ );
+ emit(
+ state.copyWith(
+ state: State.failure,
+ error: 'No folder selected.',
+ ),
+ );
+
+ return;
+ }
+
+ final folder = Directory(resultFolder);
+
+ final decryption =
+ await cryptoRepository.decryptFile(file, folder.uri, sk.ok!);
+
+ emit(
+ await decryption.foldAsync(
+ (_) async {
+ await loggerRepository.addLog(
+ const LogInfo('File successfully decrypted.\n'),
+ );
+ return state.copyWith(
+ state: State.success,
+ plainTextFile: '',
+ cipherTextFile: '',
+ );
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during decryption.'),
+ );
+ return state.copyWith(
+ state: State.failure,
+ error: error.message,
+ );
+ },
+ ),
+ );
+
+ return;
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart
new file mode 100644
index 0000000..7f4411d
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart
@@ -0,0 +1,51 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'aes_cubit.dart';
+
+@immutable
+class AESState {
+ const AESState(
+ this.state,
+ this.plainText,
+ this.cipherText,
+ this.plainTextFile,
+ this.cipherTextFile,
+ this.error,
+ );
+
+ const AESState.initial()
+ : state = State.initial,
+ plainText = null,
+ plainTextFile = null,
+ cipherText = null,
+ cipherTextFile = null,
+ error = null;
+
+ final State state;
+ final Uint8List? plainText;
+ final Uint8List? cipherText;
+ final String? plainTextFile;
+ final String? cipherTextFile;
+ final String? error;
+
+ AESState copyWith({
+ State? state,
+ Uint8List? plainText,
+ Uint8List? cipherText,
+ String? plainTextFile,
+ String? cipherTextFile,
+ String? error,
+ }) =>
+ AESState(
+ state ?? this.state,
+ plainText ?? this.plainText,
+ cipherText ?? this.cipherText,
+ plainTextFile ?? this.plainTextFile,
+ cipherTextFile ?? this.cipherTextFile,
+ error ?? this.error,
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart
new file mode 100644
index 0000000..4b96dca
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart
@@ -0,0 +1,129 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+import 'package:native_crypto_example/presentation/cipher/blocs/aes/aes_cubit.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
+import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class AESStateManagement extends CubitScreen {
+ AESStateManagement({super.key});
+
+ final TextEditingController _plainTextTextController = TextEditingController()
+ ..text = 'abc';
+
+ final TextEditingController _cipherTextTextController =
+ TextEditingController();
+
+ @override
+ AESCubit create(BuildContext context) => AESCubit(
+ sessionRepository: repo(context),
+ loggerRepository: repo(context),
+ cryptoRepository: repo(context),
+ );
+
+ @override
+ Widget onBuild(BuildContext context, AESState state) => ListView(
+ children: [
+ const Logs(),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'AES256-GCM',
+ style: AppTypography.title,
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ '''AES is a symmetric-key encryption algorithm that is widely used to encrypt sensitive data. It works by encrypting plaintext (the original data) using a secret key and a set of predefined mathematical operations (known as a cipher). The result is ciphertext (the encrypted data) that can only be decrypted and read by someone who has access to the secret key.\nGCM is a mode of operation for AES that provides both confidentiality and authenticity. It works by combining a block cipher (such as AES) with a Galois Field (a mathematical structure used to define a specific type of encryption) and a Counter (a value that is incremented for each block of data that is processed).''',
+ style: AppTypography.body,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: TextField(
+ controller: _plainTextTextController,
+ decoration: const InputDecoration(
+ hintText: 'PlainText',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Encrypt',
+ onPressed: () =>
+ bloc(context).encrypt(_plainTextTextController.text),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Alter',
+ onPressed: () => bloc(context).alterMemory(),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Decrypt',
+ onPressed: () => bloc(context).decryptFromMemory(),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'File',
+ style: AppTypography.title,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Encrypt file',
+ onPressed: () => bloc(context).encryptFile(),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Decrypt file',
+ onPressed: () => bloc(context).decryptFile(),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'External CipherText',
+ style: AppTypography.title,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: TextField(
+ controller: _cipherTextTextController,
+ decoration: const InputDecoration(
+ hintText: 'Base16 CipherText',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Decrypt',
+ onPressed: () => bloc(context)
+ .decryptFromBase16(_cipherTextTextController.text),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart
new file mode 100644
index 0000000..f57f58c
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart
@@ -0,0 +1,53 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/entities/states.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+
+part 'hash_state.dart';
+
+class HashCubit extends Cubit {
+ HashCubit({
+ required this.loggerRepository,
+ required this.cryptoRepository,
+ }) : super(const HashState.initial());
+
+ final LoggerRepository loggerRepository;
+ final CryptoRepository cryptoRepository;
+
+ FutureOr hash(Hash hasher, String message) async {
+ emit(const HashState.loading());
+ final result = await cryptoRepository.hash(hasher, message.toBytes());
+
+ emit(
+ await result.foldAsync(
+ (digest) async {
+ await loggerRepository.addLog(
+ LogInfo('Hash $message using ${hasher.algorithm.name}.\n'
+ 'Length: ${digest.length} bytes.\n'
+ 'Hex:\n${digest.toStr(to: Encoding.base16)}'),
+ );
+ return HashState.success(digest);
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during digest.'),
+ );
+ return HashState.failure(error.message);
+ },
+ ),
+ );
+
+ return;
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart
new file mode 100644
index 0000000..7eb49e3
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart
@@ -0,0 +1,32 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'hash_cubit.dart';
+
+@immutable
+class HashState {
+ const HashState.initial()
+ : state = State.initial,
+ result = null,
+ error = null;
+
+ const HashState.loading()
+ : state = State.loading,
+ result = null,
+ error = null;
+
+ const HashState.failure(this.error)
+ : state = State.failure,
+ result = null;
+
+ const HashState.success(this.result)
+ : state = State.success,
+ error = null;
+
+ final State state;
+ final Uint8List? result;
+ final String? error;
+}
diff --git a/packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart b/packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart
new file mode 100644
index 0000000..c2c5c9a
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart
@@ -0,0 +1,88 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/presentation/hash/blocs/hash/hash_cubit.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
+import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class HashStateManagement extends CubitScreen {
+ HashStateManagement({super.key});
+
+ final TextEditingController _hashTextController = TextEditingController()
+ ..text = 'abc';
+
+ @override
+ HashCubit create(BuildContext context) => HashCubit(
+ loggerRepository: repo(context),
+ cryptoRepository: repo(context),
+ );
+
+ @override
+ Widget onBuild(BuildContext context, HashState state) => ListView(
+ children: [
+ const Logs(),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'Hash',
+ style: AppTypography.title,
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ '''SHA-256, SHA-384, and SHA-512 are cryptographic hash functions. They are used to create a fixed-size output (known as a hash or digest) from an input of any size. The output of a hash function is deterministic, which means that the same input will always produce the same output.''',
+ style: AppTypography.body,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: TextField(
+ controller: _hashTextController,
+ decoration: const InputDecoration(
+ hintText: 'Message',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'SHA256',
+ onPressed: () => bloc(context).hash(
+ Sha256(),
+ _hashTextController.text,
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'SHA384',
+ onPressed: () => bloc(context).hash(
+ Sha384(),
+ _hashTextController.text,
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'SHA512',
+ onPressed: () => bloc(context).hash(
+ Sha512(),
+ _hashTextController.text,
+ ),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart
new file mode 100644
index 0000000..b007ce4
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart
@@ -0,0 +1,53 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+
+part 'mode_switcher_state.dart';
+
+class ModeSwitcherCubit extends Cubit {
+ ModeSwitcherCubit(
+ this.sessionRepository,
+ this.cryptoRepository,
+ ) : super(const ModeSwitcherState(NativeCryptoMode()));
+
+ SessionRepository sessionRepository;
+ CryptoRepository cryptoRepository;
+
+ FutureOr switchMode() async {
+ final currentMode = await sessionRepository.getCurrentMode();
+ Mode? newMode;
+
+ if (currentMode.isOk) {
+ if (currentMode.ok == const NativeCryptoMode()) {
+ newMode = const PointyCastleMode();
+ } else {
+ newMode = const NativeCryptoMode();
+ }
+
+ sessionRepository.setCurrentMode(newMode);
+ if (cryptoRepository is CryptoRepositorySwitchableImpl) {
+ (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
+ }
+ } else {
+ newMode = const NativeCryptoMode();
+ sessionRepository.setCurrentMode(newMode);
+
+ if (cryptoRepository is CryptoRepositorySwitchableImpl) {
+ (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode;
+ }
+ }
+
+ emit(ModeSwitcherState(newMode));
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart
new file mode 100644
index 0000000..9eae7c3
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart
@@ -0,0 +1,26 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'mode_switcher_cubit.dart';
+
+@immutable
+class ModeSwitcherState {
+ const ModeSwitcherState(this.currentMode);
+
+ final Mode currentMode;
+
+ @override
+ bool operator ==(covariant ModeSwitcherState other) {
+ if (identical(this, other)) {
+ return true;
+ }
+
+ return other.currentMode == currentMode;
+ }
+
+ @override
+ int get hashCode => currentMode.hashCode;
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart
new file mode 100644
index 0000000..ee5263a
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart
@@ -0,0 +1,20 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+part 'navigation_bar_state.dart';
+
+class NavigationBarCubit extends Cubit {
+ NavigationBarCubit() : super(const NavigationBarState(0));
+
+ FutureOr changePage(int page) {
+ emit(NavigationBarState(page));
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart
new file mode 100644
index 0000000..abec21c
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart
@@ -0,0 +1,14 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'navigation_bar_cubit.dart';
+
+@immutable
+class NavigationBarState {
+ const NavigationBarState(this.index);
+
+ final int index;
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart
new file mode 100644
index 0000000..fbd378d
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart
@@ -0,0 +1,67 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/presentation/benchmark/state_management/benchmark_state_management.dart';
+import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart';
+import 'package:native_crypto_example/presentation/hash/state_management/hash_state_management.dart';
+import 'package:native_crypto_example/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/app_bar_state_management.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart';
+import 'package:native_crypto_example/presentation/kdf/state_management/key_derivation_state_management.dart';
+import 'package:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class HomeStateManagement
+ extends CubitScreen {
+ HomeStateManagement({super.key});
+
+ final List _children = [
+ KeyDerivationStateManagement(),
+ HashStateManagement(),
+ AESStateManagement(),
+ TestVectorsStateManagement(),
+ const BenchmarkStateManagement(),
+ ];
+
+ @override
+ NavigationBarCubit create(BuildContext context) => NavigationBarCubit();
+
+ @override
+ Widget onBuild(BuildContext context, NavigationBarState state) => Scaffold(
+ appBar: const PreferredSize(
+ preferredSize: Size(double.infinity, 60),
+ child: AppBarStateManagement(),
+ ),
+ body: _children[state.index],
+ bottomNavigationBar: BottomNavigationBarStateManagement(
+ onTap: (page) => bloc(context).changePage(page), // new
+ currentIndex: state.index, // new
+ items: const [
+ BottomNavigationBarItem(
+ icon: Icon(Icons.vpn_key),
+ label: 'Key',
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.tag),
+ label: 'Hash',
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.lock),
+ label: 'Encryption',
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.rule_rounded),
+ label: 'Tests',
+ ),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.timer),
+ label: 'Benchmark',
+ ),
+ ],
+ ),
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart
new file mode 100644
index 0000000..6b522a4
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart
@@ -0,0 +1,33 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/domain/entities/mode.dart';
+import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class AppBarStateManagement
+ extends CubitConsumerScreen {
+ const AppBarStateManagement({super.key});
+
+ @override
+ Widget onBuild(BuildContext context, ModeSwitcherState state) => AppBar(
+ centerTitle: true,
+ title: Text(
+ state.currentMode == const NativeCryptoMode()
+ ? 'NativeCrypto'
+ : 'PointyCastle',
+ ),
+ backgroundColor: state.currentMode.primaryColor,
+ actions: [
+ Switch(
+ activeColor: Colors.white,
+ value: state.currentMode == const NativeCryptoMode(),
+ onChanged: (_) => bloc(context).switchMode(),
+ )
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart
new file mode 100644
index 0000000..5483ad1
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart
@@ -0,0 +1,35 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class BottomNavigationBarStateManagement
+ extends CubitConsumerScreen {
+ const BottomNavigationBarStateManagement({
+ required this.items,
+ this.currentIndex = 0,
+ this.onTap,
+ super.key,
+ });
+
+ final void Function(int)? onTap;
+ final int currentIndex;
+ final List items;
+
+ @override
+ Widget onBuild(BuildContext context, ModeSwitcherState state) =>
+ BottomNavigationBar(
+ selectedItemColor: state.currentMode.primaryColor,
+ unselectedItemColor: Colors.black,
+ showUnselectedLabels: true,
+ onTap: onTap,
+ currentIndex: currentIndex,
+ items: items,
+ type: BottomNavigationBarType.shifting,
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart
new file mode 100644
index 0000000..97b0f8a
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart
@@ -0,0 +1,38 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class ButtonStateManagement
+ extends CubitConsumerScreen {
+ const ButtonStateManagement({
+ required this.label,
+ this.onPressed,
+ super.key,
+ });
+
+ final void Function()? onPressed;
+ final String label;
+
+ @override
+ Widget onBuild(BuildContext context, ModeSwitcherState state) {
+ context.watch();
+ return ElevatedButton(
+ onPressed: onPressed,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: state.currentMode.primaryColor,
+ ),
+ child: Text(
+ label,
+ style: AppTypography.body,
+ ),
+ );
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/home/widgets/blank.dart b/packages/native_crypto/example/lib/presentation/home/widgets/blank.dart
new file mode 100644
index 0000000..3a5835e
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/home/widgets/blank.dart
@@ -0,0 +1,15 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+
+class Blank extends StatelessWidget {
+ const Blank({super.key});
+
+ @override
+ Widget build(BuildContext context) =>
+ const Center(child: Text('Nothing to show'));
+}
diff --git a/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart
new file mode 100644
index 0000000..41f9c48
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart
@@ -0,0 +1,112 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/entities/states.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+
+part 'key_derivation_state.dart';
+
+class KeyDerivationCubit extends Cubit {
+ KeyDerivationCubit({
+ required this.sessionRepository,
+ required this.loggerRepository,
+ required this.cryptoRepository,
+ }) : super(const KeyDerivationState.initial());
+ final SessionRepository sessionRepository;
+ final LoggerRepository loggerRepository;
+ final CryptoRepository cryptoRepository;
+
+ FutureOr generate() async {
+ emit(const KeyDerivationState.loading());
+ final result = await cryptoRepository.generateSecureRandom(32);
+
+ emit(
+ await result.foldAsync(
+ (key) async {
+ await sessionRepository.setSessionKey(key);
+ await loggerRepository.addLog(
+ LogInfo('SecretKey successfully generated.\n'
+ 'Length: ${key.bytes.length} bytes.\n'
+ 'Hex:\n${key.base16}'),
+ );
+ return KeyDerivationState.success(key.bytes);
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during key generation.'),
+ );
+ return KeyDerivationState.failure(error.message);
+ },
+ ),
+ );
+
+ return;
+ }
+
+ FutureOr pbkdf2(String password) async {
+ emit(const KeyDerivationState.loading());
+ if (password.isEmpty) {
+ const error = 'Password is empty';
+ await loggerRepository.addLog(
+ const LogError(error),
+ );
+ emit(const KeyDerivationState.failure(error));
+ }
+ final result =
+ await cryptoRepository.deriveKeyFromPassword(password, salt: 'salt');
+
+ emit(
+ await result.foldAsync(
+ (key) async {
+ await sessionRepository.setSessionKey(key);
+ await loggerRepository.addLog(
+ LogInfo('SecretKey successfully derivated from $password.\n'
+ 'Length: ${key.bytes.length} bytes.\n'
+ 'Hex:\n${key.base16}'),
+ );
+ return KeyDerivationState.success(key.bytes);
+ },
+ (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during key derivation.'),
+ );
+ return KeyDerivationState.failure(error.message);
+ },
+ ),
+ );
+
+ return;
+ }
+
+ FutureOr getSessionKey() async {
+ emit(const KeyDerivationState.loading());
+ final sk = await sessionRepository.getSessionKey();
+ emit(
+ await sk.foldAsync((key) async {
+ await loggerRepository.addLog(
+ LogInfo('Session key successfully retreived.\n'
+ 'Length: ${key.bytes.length} bytes.\n'
+ 'Hex:\n${key.base16}'),
+ );
+ return KeyDerivationState.success(key.bytes);
+ }, (error) async {
+ await loggerRepository.addLog(
+ LogError(error.message ?? 'Error during key retrieving.'),
+ );
+ return KeyDerivationState.failure(error.message);
+ }),
+ );
+
+ return;
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart
new file mode 100644
index 0000000..c8b7e6d
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart
@@ -0,0 +1,32 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'key_derivation_cubit.dart';
+
+@immutable
+class KeyDerivationState {
+ const KeyDerivationState.initial()
+ : state = State.initial,
+ result = null,
+ error = null;
+
+ const KeyDerivationState.loading()
+ : state = State.loading,
+ result = null,
+ error = null;
+
+ const KeyDerivationState.failure(this.error)
+ : state = State.failure,
+ result = null;
+
+ const KeyDerivationState.success(this.result)
+ : state = State.success,
+ error = null;
+
+ final State state;
+ final Uint8List? result;
+ final String? error;
+}
diff --git a/packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart b/packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart
new file mode 100644
index 0000000..fff27cc
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart
@@ -0,0 +1,102 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/domain/repositories/session_repository.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
+import 'package:native_crypto_example/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart';
+import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class KeyDerivationStateManagement
+ extends CubitScreen {
+ KeyDerivationStateManagement({super.key});
+
+ final TextEditingController _pwdTextController = TextEditingController()
+ ..text = 'password';
+
+ @override
+ KeyDerivationCubit create(BuildContext context) => KeyDerivationCubit(
+ sessionRepository: repo(context),
+ loggerRepository: repo(context),
+ cryptoRepository: repo(context),
+ );
+
+ @override
+ Widget onBuild(BuildContext context, KeyDerivationState state) => ListView(
+ children: [
+ const Logs(),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'Random secret key',
+ style: AppTypography.title,
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ '''A secret key is a piece of information that is used to encrypt and decrypt messages. In symmetric-key encryption, the same secret key is used for both encrypting the original data and for decrypting the encrypted data. The key is kept secret so that only authorized parties can read the encrypted messages.''',
+ style: AppTypography.body,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Generate secret key',
+ onPressed: () => bloc(context).generate(),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'PBKDF2',
+ style: AppTypography.title,
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ '''PBKDF2 (Password-Based Key Derivation Function 2) is a key derivation function that is designed to be computationally expensive and memory-intensive, in order to slow down the process of guessing a password. This makes it more difficult for an attacker to guess a password by using a brute-force attack, which involves trying every possible combination of characters until the correct password is found.''',
+ style: AppTypography.body,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: TextField(
+ controller: _pwdTextController,
+ decoration: const InputDecoration(
+ hintText: 'Password',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Compute PBKDF2',
+ onPressed: () => bloc(context).pbkdf2(_pwdTextController.text),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'Session',
+ style: AppTypography.title,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Retrieve session key',
+ onPressed: () => bloc(context).getSessionKey(),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart
new file mode 100644
index 0000000..fd5cd43
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart
@@ -0,0 +1,49 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:wyatt_architecture/wyatt_architecture.dart';
+import 'package:wyatt_type_utils/wyatt_type_utils.dart';
+
+part 'output_state.dart';
+
+class OutputCubit extends Cubit {
+ OutputCubit(this.loggerRepository) : super(const OutputState.empty()) {
+ logSubscription = loggerRepository.streamLogs().listen((message) {
+ if (message.isOk) {
+ onMessage(message.ok!);
+ }
+ });
+ }
+
+ final LoggerRepository loggerRepository;
+ late StreamSubscription, AppException>>
+ logSubscription;
+
+ FutureOr add(LogMessage message) {
+ loggerRepository.addLog(message);
+ }
+
+ FutureOr clear() {
+ loggerRepository.clearLog();
+ emit(const OutputState.empty());
+ }
+
+ FutureOr onMessage(Map entries) {
+ emit(OutputState(entries: entries));
+ }
+
+ @override
+ Future close() {
+ logSubscription.cancel();
+ return super.close();
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart
new file mode 100644
index 0000000..6cb82c3
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart
@@ -0,0 +1,32 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'output_cubit.dart';
+
+@immutable
+class OutputState {
+ const OutputState({
+ required this.entries,
+ });
+
+ const OutputState.empty() : entries = const {};
+
+ final Map entries;
+
+ @override
+ String toString() {
+ final StringBuffer buffer = StringBuffer();
+ entries.forEach((key, value) {
+ buffer
+ ..write(value.prefix)
+ ..write(' at ')
+ ..write(key.toIso8601String())
+ ..write(':\t')
+ ..writeln(value.message);
+ });
+ return buffer.toString();
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart b/packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart
new file mode 100644
index 0000000..1512987
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart
@@ -0,0 +1,76 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class OutputStateManagement
+ extends CubitConsumerScreen {
+ OutputStateManagement({super.key});
+
+ final ScrollController _scrollController = ScrollController();
+
+ @override
+ Widget onBuild(BuildContext context, OutputState state) {
+ // Auto scroll
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (_scrollController.hasClients) {
+ _scrollController.animateTo(
+ _scrollController.position.maxScrollExtent,
+ duration: const Duration(milliseconds: 100),
+ curve: Curves.easeOut,
+ );
+ }
+ });
+
+ return SizedBox(
+ height: 250,
+ child: ListView.separated(
+ controller: _scrollController,
+ itemCount: state.entries.length,
+ separatorBuilder: (context, index) => const Padding(
+ padding: EdgeInsets.all(8),
+ child: SizedBox(
+ height: 1,
+ child: ColoredBox(color: Colors.black),
+ ),
+ ),
+ itemBuilder: (context, index) => Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ state.entries.values.elementAt(index).prefix,
+ style: TextStyle(
+ color: state.entries.values.elementAt(index).color,
+ ),
+ ),
+ Text(
+ state.entries.keys.elementAt(index).toIso8601String(),
+ style: TextStyle(
+ color: state.entries.values.elementAt(index).color,
+ ),
+ )
+ ],
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: SelectableText(
+ state.entries.values.elementAt(index).message,
+ style: TextStyle(
+ color: state.entries.values.elementAt(index).color,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart b/packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart
new file mode 100644
index 0000000..ecbc128
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart
@@ -0,0 +1,22 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
+import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class ClearButtonStateManagement
+ extends CubitConsumerScreen {
+ const ClearButtonStateManagement({super.key});
+
+ @override
+ Widget onBuild(BuildContext context, OutputState state) =>
+ ButtonStateManagement(
+ label: 'Clear',
+ onPressed: () => bloc(context).clear(),
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/output/widgets/logs.dart b/packages/native_crypto/example/lib/presentation/output/widgets/logs.dart
new file mode 100644
index 0000000..a623e1a
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/output/widgets/logs.dart
@@ -0,0 +1,44 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/presentation/output/state_management/output_state_management.dart';
+import 'package:native_crypto_example/presentation/output/state_management/widgets/clear_button_state_management.dart';
+
+class Logs extends StatelessWidget {
+ const Logs({super.key});
+
+ @override
+ Widget build(BuildContext context) => Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: const [
+ Text(
+ 'Logs',
+ style: AppTypography.title,
+ ),
+ ClearButtonStateManagement(),
+ ],
+ ),
+ ),
+ const SizedBox(
+ height: 2,
+ width: double.infinity,
+ child: ColoredBox(color: Colors.black),
+ ),
+ OutputStateManagement(),
+ const SizedBox(
+ height: 2,
+ width: double.infinity,
+ child: ColoredBox(color: Colors.black),
+ ),
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart
new file mode 100644
index 0000000..2d84265
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart
@@ -0,0 +1,204 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:native_crypto/native_crypto.dart';
+import 'package:native_crypto_example/domain/entities/log_message.dart';
+import 'package:native_crypto_example/domain/entities/states.dart';
+import 'package:native_crypto_example/domain/entities/test_vector.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+
+part 'test_vectors_state.dart';
+
+class TestVectorsCubit extends Cubit {
+ TestVectorsCubit({
+ required this.loggerRepository,
+ required this.cryptoRepository,
+ }) : super(const TestVectorsState.initial());
+ final LoggerRepository loggerRepository;
+ final CryptoRepository cryptoRepository;
+
+ final tests = [
+ TestVector(
+ key: 'b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4',
+ nonce: '516c33929df5a3284ff463d7',
+ plainText: '',
+ cipherText: '',
+ tag: 'bdc1ac884d332457a1d2664f168c76f0',
+ ),
+ TestVector(
+ key: '5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f',
+ nonce: '770ac1a5a3d476d5d96944a1',
+ plainText: '',
+ cipherText: '',
+ tag: '196d691e1047093ca4b3d2ef4baba216',
+ ),
+ TestVector(
+ key: '7620b79b17b21b06d97019aa70e1ca105e1c03d2a0cf8b20b5a0ce5c3903e548',
+ nonce: '60f56eb7a4b38d4f03395511',
+ plainText: '',
+ cipherText: '',
+ tag: 'f570c38202d94564bab39f75617bc87a',
+ ),
+ TestVector(
+ key: '31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22',
+ nonce: '0d18e06c7c725ac9e362e1ce',
+ plainText: '2db5168e932556f8089a0622981d017d',
+ cipherText: 'fa4362189661d163fcd6a56d8bf0405a',
+ tag: 'd636ac1bbedd5cc3ee727dc2ab4a9489',
+ ),
+ TestVector(
+ key: '460fc864972261c2560e1eb88761ff1c992b982497bd2ac36c04071cbb8e5d99',
+ nonce: '8a4a16b9e210eb68bcb6f58d',
+ plainText: '99e4e926ffe927f691893fb79a96b067',
+ cipherText: '133fc15751621b5f325c7ff71ce08324',
+ tag: 'ec4e87e0cf74a13618d0b68636ba9fa7',
+ ),
+ TestVector(
+ key: 'f78a2ba3c5bd164de134a030ca09e99463ea7e967b92c4b0a0870796480297e5',
+ nonce: '2bb92fcb726c278a2fa35a88',
+ plainText: 'f562509ed139a6bbe7ab545ac616250c',
+ cipherText: 'e2f787996e37d3b47294bf7ebba5ee25',
+ tag: '00f613eee9bdad6c9ee7765db1cb45c0',
+ ),
+ ];
+
+ FutureOr launchAllTests() async {
+ emit(const TestVectorsState.loading());
+ int i = 0;
+ for (final test in tests) {
+ final sk = SecretKey(test.keyBytes);
+ final iv = test.nonceBytes;
+
+ // Encryption
+ final encryption =
+ await cryptoRepository.encryptWithIV(test.plainTextBytes, sk, iv);
+ final testCipherText = await encryption
+ .foldAsync((cipherText) async => cipherText, (error) async {
+ await loggerRepository.addLog(
+ LogError(
+ 'TEST $i :: ${error.message ?? 'Error during encryption.'} ❌',
+ ),
+ );
+ });
+
+ final encryptionSuccess = test.validateCipherText(testCipherText);
+
+ if (encryptionSuccess) {
+ await loggerRepository.addLog(
+ LogInfo('TEST $i :: Encryption test passed ✅'),
+ );
+ } else {
+ await loggerRepository.addLog(
+ LogError('TEST $i :: Encryption test failed ❌'),
+ );
+ }
+
+ // Decryption
+ final decryption =
+ await cryptoRepository.decrypt(test.cipherTextBytes, sk);
+ final testPlainText = await decryption
+ .foldAsync((plainText) async => plainText, (error) async {
+ await loggerRepository.addLog(
+ LogError(
+ 'TEST $i :: ${error.message ?? 'Error during decryption. ❌'}',
+ ),
+ );
+ });
+
+ final decryptionSuccess = test.validatePlainText(testPlainText);
+
+ if (decryptionSuccess) {
+ await loggerRepository.addLog(
+ LogInfo('TEST $i :: Decryption test passed ✅'),
+ );
+ } else {
+ await loggerRepository.addLog(
+ LogError('TEST $i :: Decryption test failed ❌'),
+ );
+ }
+ i++;
+ }
+
+ if (i == tests.length) {
+ emit(const TestVectorsState.success());
+ } else {
+ emit(const TestVectorsState.failure('Error during the test suite'));
+ }
+
+ return;
+ }
+
+ FutureOr launchTest(String number) async {
+ try {
+ final id = int.parse(number);
+
+ final test = tests.elementAt(id);
+
+ final sk = SecretKey(test.keyBytes);
+ final iv = test.nonceBytes;
+
+ // Encryption
+ final encryption =
+ await cryptoRepository.encryptWithIV(test.plainTextBytes, sk, iv);
+ final testCipherText = await encryption
+ .foldAsync((cipherText) async => cipherText, (error) async {
+ await loggerRepository.addLog(
+ LogError(
+ 'TEST $id :: ${error.message ?? 'Error during encryption.'} ❌',
+ ),
+ );
+ });
+
+ final encryptionSuccess = test.validateCipherText(testCipherText);
+
+ if (encryptionSuccess) {
+ await loggerRepository.addLog(
+ LogInfo('TEST $id :: Encryption test passed ✅'),
+ );
+ } else {
+ await loggerRepository.addLog(
+ LogError('TEST $id :: Encryption test failed ❌'),
+ );
+ }
+
+ // Decryption
+ final decryption =
+ await cryptoRepository.decrypt(test.cipherTextBytes, sk);
+ final testPlainText = await decryption
+ .foldAsync((plainText) async => plainText, (error) async {
+ await loggerRepository.addLog(
+ LogError(
+ 'TEST $id :: ${error.message ?? 'Error during decryption. ❌'}',
+ ),
+ );
+ });
+
+ final decryptionSuccess = test.validatePlainText(testPlainText);
+
+ if (decryptionSuccess) {
+ await loggerRepository.addLog(
+ LogInfo('TEST $id :: Decryption test passed ✅'),
+ );
+ } else {
+ await loggerRepository.addLog(
+ LogError('TEST $id :: Decryption test failed ❌'),
+ );
+ }
+ emit(const TestVectorsState.success());
+ } catch (e) {
+ final error = 'Invalid vector number: $number';
+ await loggerRepository.addLog(LogError(error));
+ emit(TestVectorsState.failure(error));
+ }
+
+ return;
+ }
+}
diff --git a/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart
new file mode 100644
index 0000000..7b06271
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart
@@ -0,0 +1,27 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+part of 'test_vectors_cubit.dart';
+
+@immutable
+class TestVectorsState {
+ const TestVectorsState.initial()
+ : state = State.initial,
+ error = null;
+
+ const TestVectorsState.loading()
+ : state = State.loading,
+ error = null;
+
+ const TestVectorsState.failure(this.error) : state = State.failure;
+
+ const TestVectorsState.success()
+ : state = State.success,
+ error = null;
+
+ final State state;
+ final String? error;
+}
diff --git a/packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart b/packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart
new file mode 100644
index 0000000..282b52e
--- /dev/null
+++ b/packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart
@@ -0,0 +1,74 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto_example/core/typography.dart';
+import 'package:native_crypto_example/domain/repositories/crypto_repository.dart';
+import 'package:native_crypto_example/domain/repositories/logger_repository.dart';
+import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart';
+import 'package:native_crypto_example/presentation/output/widgets/logs.dart';
+import 'package:native_crypto_example/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart';
+import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
+
+class TestVectorsStateManagement
+ extends CubitScreen {
+ TestVectorsStateManagement({super.key});
+
+ final TextEditingController _vectorNumberTextController =
+ TextEditingController();
+
+ @override
+ TestVectorsCubit create(BuildContext context) => TestVectorsCubit(
+ loggerRepository: repo(context),
+ cryptoRepository: repo(context),
+ );
+
+ @override
+ Widget onBuild(BuildContext context, TestVectorsState state) => ListView(
+ children: [
+ const Logs(),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ 'Test vectors',
+ style: AppTypography.title,
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.all(8),
+ child: Text(
+ '''In cryptography, a test vector is a set of input values that is used to test the correct implementation and operation of a cryptographic algorithm or system. Test vectors are usually provided by the creators of the algorithm, or by national standardization organizations, and can be used to verify that an implementation of the algorithm is correct and produces the expected output for a given set of inputs.''',
+ style: AppTypography.body,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'All Tests',
+ onPressed: () => bloc(context).launchAllTests(),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: TextField(
+ controller: _vectorNumberTextController,
+ keyboardType: TextInputType.number,
+ decoration: const InputDecoration(
+ hintText: 'Vector number',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: ButtonStateManagement(
+ label: 'Specific Test',
+ onPressed: () =>
+ bloc(context).launchTest(_vectorNumberTextController.text),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/packages/native_crypto/example/lib/session.dart b/packages/native_crypto/example/lib/session.dart
deleted file mode 100644
index 7636866..0000000
--- a/packages/native_crypto/example/lib/session.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: session.dart
-// Created Date: 28/12/2021 13:54:29
-// Last Modified: 28/12/2021 13:58:49
-// -----
-// Copyright (c) 2021
-
-import 'dart:typed_data';
-
-import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:native_crypto/native_crypto.dart';
-
-class Session {
- SecretKey secretKey;
- Session() : secretKey = SecretKey(Uint8List(0));
-
- void setKey(SecretKey sk) {
- secretKey = sk;
- }
-}
-
-// Providers
-
-final sessionProvider = StateProvider((ref) => Session());
diff --git a/packages/native_crypto/example/lib/widgets/button.dart b/packages/native_crypto/example/lib/widgets/button.dart
deleted file mode 100644
index 2244bb4..0000000
--- a/packages/native_crypto/example/lib/widgets/button.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: button.dart
-// Created Date: 28/12/2021 13:31:17
-// Last Modified: 28/12/2021 13:31:34
-// -----
-// Copyright (c) 2021
-
-import 'package:flutter/material.dart';
-
-class Button extends StatelessWidget {
- final void Function() onPressed;
- final String label;
-
- const Button(this.onPressed, this.label, {Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return ElevatedButton(
- onPressed: onPressed,
- style: TextButton.styleFrom(
- primary: Colors.blue,
- ),
- child: Text(
- label,
- style: const TextStyle(color: Colors.white),
- ),
- );
- }
-}
diff --git a/packages/native_crypto/example/lib/widgets/output.dart b/packages/native_crypto/example/lib/widgets/output.dart
deleted file mode 100644
index 1dc9b70..0000000
--- a/packages/native_crypto/example/lib/widgets/output.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: output.dart
-// Created Date: 28/12/2021 13:31:39
-// Last Modified: 25/05/2022 16:39:39
-// -----
-// Copyright (c) 2021
-
-import 'package:flutter/material.dart';
-
-// ignore: must_be_immutable
-class Output extends StatelessWidget {
- late TextEditingController controller;
- final bool large;
- final bool editable;
-
- Output({
- Key? key,
- TextEditingController? controller,
- this.large = false,
- this.editable = false,
- }) : super(key: key) {
- this.controller = controller ?? TextEditingController();
- }
-
- void print(String message) {
- debugPrint(message);
- controller.text = message;
- }
-
- void append(String message) {
- debugPrint(message);
- controller.text += message;
- }
-
- void appendln(String message) {
- debugPrint(message);
- controller.text += message + "\n";
- }
-
- void clear() {
- controller.clear();
- }
-
- String read() {
- return controller.text;
- }
-
- @override
- Widget build(BuildContext context) {
- return TextField(
- enableInteractiveSelection: true,
- readOnly: editable ? false : true,
- minLines: large ? 3 : 1,
- maxLines: large ? 500 : 5,
- decoration: const InputDecoration(border: OutlineInputBorder()),
- controller: controller,
- );
- }
-}
diff --git a/packages/native_crypto/example/pubspec.yaml b/packages/native_crypto/example/pubspec.yaml
index 1e16cd6..33e895d 100644
--- a/packages/native_crypto/example/pubspec.yaml
+++ b/packages/native_crypto/example/pubspec.yaml
@@ -3,10 +3,10 @@ description: Demonstrates how to use the native_crypto plugin.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
-publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+publish_to: "none" # Remove this line if you wish to publish to pub.dev
environment:
- sdk: ">=2.15.0 <3.0.0"
+ sdk: ">=2.17.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@@ -15,40 +15,37 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
- flutter:
- sdk: flutter
+ flutter: { sdk: flutter }
+ native_crypto: { path: ../ }
+ cupertino_icons: ^1.0.5
+ flutter_riverpod: ^2.1.3
+ pointycastle: ^3.6.2
+ flutter_bloc: ^8.1.1
- native_crypto:
- # When depending on this package from a real application you should use:
- # native_crypto: ^x.y.z
- # See https://dart.dev/tools/pub/dependencies#version-constraints
- # The example app is bundled with the plugin so we use a path dependency on
- # the parent directory to use the current plugin's version.
- path: ../
-
- # The following adds the Cupertino Icons font to your application.
- # Use with the CupertinoIcons class for iOS style icons.
- cupertino_icons: ^1.0.2
- flutter_riverpod: ^1.0.3
- pointycastle: ^3.6.0
+ wyatt_architecture:
+ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
+ version: 0.1.0+1
+ wyatt_type_utils:
+ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
+ version: 0.0.4
+ wyatt_bloc_helper:
+ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
+ version: 2.0.0
+ get_it: ^7.2.0
+ file_picker: ^5.2.7
dev_dependencies:
- flutter_test:
- sdk: flutter
+ flutter_test: { sdk: flutter }
- # The "flutter_lints" package below contains a set of recommended lints to
- # encourage good coding practices. The lint set provided by the package is
- # activated in the `analysis_options.yaml` file located at the root of your
- # package. See that file for information about deactivating specific lint
- # rules and activating additional ones.
- flutter_lints: ^2.0.0
+ wyatt_analysis:
+ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
+ version: 2.4.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
-
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
diff --git a/packages/native_crypto/example/pubspec_overrides.yaml b/packages/native_crypto/example/pubspec_overrides.yaml
new file mode 100644
index 0000000..f4eef6c
--- /dev/null
+++ b/packages/native_crypto/example/pubspec_overrides.yaml
@@ -0,0 +1,12 @@
+# melos_managed_dependency_overrides: native_crypto,native_crypto_android,native_crypto_ios,native_crypto_platform_interface,native_crypto_web
+dependency_overrides:
+ native_crypto:
+ path: ..
+ native_crypto_android:
+ path: ../../native_crypto_android
+ native_crypto_ios:
+ path: ../../native_crypto_ios
+ native_crypto_platform_interface:
+ path: ../../native_crypto_platform_interface
+ native_crypto_web:
+ path: ../../native_crypto_web
diff --git a/packages/native_crypto/example/web/favicon.png b/packages/native_crypto/example/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
Binary files /dev/null and b/packages/native_crypto/example/web/favicon.png differ
diff --git a/packages/native_crypto/example/web/icons/Icon-192.png b/packages/native_crypto/example/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
Binary files /dev/null and b/packages/native_crypto/example/web/icons/Icon-192.png differ
diff --git a/packages/native_crypto/example/web/icons/Icon-512.png b/packages/native_crypto/example/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
Binary files /dev/null and b/packages/native_crypto/example/web/icons/Icon-512.png differ
diff --git a/packages/native_crypto/example/web/icons/Icon-maskable-192.png b/packages/native_crypto/example/web/icons/Icon-maskable-192.png
new file mode 100644
index 0000000..eb9b4d7
Binary files /dev/null and b/packages/native_crypto/example/web/icons/Icon-maskable-192.png differ
diff --git a/packages/native_crypto/example/web/icons/Icon-maskable-512.png b/packages/native_crypto/example/web/icons/Icon-maskable-512.png
new file mode 100644
index 0000000..d69c566
Binary files /dev/null and b/packages/native_crypto/example/web/icons/Icon-maskable-512.png differ
diff --git a/packages/native_crypto/example/web/index.html b/packages/native_crypto/example/web/index.html
new file mode 100644
index 0000000..41b3bc3
--- /dev/null
+++ b/packages/native_crypto/example/web/index.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ example
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/native_crypto/example/web/manifest.json b/packages/native_crypto/example/web/manifest.json
new file mode 100644
index 0000000..096edf8
--- /dev/null
+++ b/packages/native_crypto/example/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "example",
+ "short_name": "example",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/packages/native_crypto/example/web/pkg b/packages/native_crypto/example/web/pkg
new file mode 120000
index 0000000..033f93b
--- /dev/null
+++ b/packages/native_crypto/example/web/pkg
@@ -0,0 +1 @@
+../../../native-crypto-rust/pkg/
\ No newline at end of file
diff --git a/packages/native_crypto/lib/native_crypto.dart b/packages/native_crypto/lib/native_crypto.dart
index 2b294a4..2a7dd10 100644
--- a/packages/native_crypto/lib/native_crypto.dart
+++ b/packages/native_crypto/lib/native_crypto.dart
@@ -1,11 +1,8 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: native_crypto.dart
-// Created Date: 16/12/2021 16:28:00
-// Last Modified: 26/05/2022 12:10:42
-// -----
-// Copyright (c) 2021
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
/// Fast and powerful cryptographic functions
/// thanks to javax.crypto, CommonCrypto and CryptoKit.
@@ -13,18 +10,13 @@
/// Author: Hugo Pointcheval
library native_crypto;
-export 'package:native_crypto_platform_interface/src/utils/exception.dart';
+export 'package:native_crypto_platform_interface/src/core/exceptions/exception.dart';
export 'src/builders/builders.dart';
export 'src/ciphers/ciphers.dart';
export 'src/core/core.dart';
-export 'src/interfaces/interfaces.dart';
-export 'src/kdf/kdf.dart';
-export 'src/keys/keys.dart';
-// Utils
-export 'src/utils/cipher_algorithm.dart';
-export 'src/utils/hash_algorithm.dart';
-export 'src/utils/kdf_algorithm.dart';
-
-// ignore: constant_identifier_names
-const String AUTHOR = 'Hugo Pointcheval';
+export 'src/digest/digest.dart';
+export 'src/domain/domain.dart';
+export 'src/kdf/pbkdf2.dart';
+export 'src/keys/secret_key.dart';
+export 'src/random/secure_random.dart';
diff --git a/packages/native_crypto/lib/native_crypto_ext.dart b/packages/native_crypto/lib/native_crypto_ext.dart
deleted file mode 100644
index 0e9502e..0000000
--- a/packages/native_crypto/lib/native_crypto_ext.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: native_crypto_ext.dart
-// Created Date: 26/05/2022 19:36:54
-// Last Modified: 26/05/2022 19:38:44
-// -----
-// Copyright (c) 2022
-
-export 'src/utils/encoding.dart';
-export 'src/utils/extensions.dart';
diff --git a/packages/native_crypto/lib/src/builders/builders.dart b/packages/native_crypto/lib/src/builders/builders.dart
index d846197..df13d09 100644
--- a/packages/native_crypto/lib/src/builders/builders.dart
+++ b/packages/native_crypto/lib/src/builders/builders.dart
@@ -1,10 +1,8 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: builders.dart
-// Created Date: 23/05/2022 22:56:03
-// Last Modified: 26/05/2022 19:22:19
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
export 'decryption_builder.dart';
+export 'encryption_builder.dart';
diff --git a/packages/native_crypto/lib/src/builders/decryption_builder.dart b/packages/native_crypto/lib/src/builders/decryption_builder.dart
index 998fdcf..c9d6dbe 100644
--- a/packages/native_crypto/lib/src/builders/decryption_builder.dart
+++ b/packages/native_crypto/lib/src/builders/decryption_builder.dart
@@ -1,46 +1,60 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: decryption_builder.dart
-// Created Date: 26/05/2022 19:07:52
-// Last Modified: 26/05/2022 19:21:00
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
import 'dart:typed_data';
import 'package:flutter/material.dart';
-import 'package:native_crypto/src/core/cipher_text_wrapper.dart';
-import 'package:native_crypto/src/interfaces/cipher.dart';
+import 'package:native_crypto/src/core/utils/cipher_text.dart';
-class DecryptionBuilder extends StatelessWidget {
- final Cipher cipher;
- final CipherTextWrapper data;
- final Widget Function(BuildContext context) onLoading;
- final Widget Function(BuildContext context, Object error) onError;
- final Widget Function(BuildContext context, Uint8List plainText) onSuccess;
+import 'package:native_crypto/src/domain/cipher.dart';
+import 'package:native_crypto/src/domain/cipher_chunk.dart';
+/// {@template decryption_builder}
+/// A [StatelessWidget] that builds a [FutureBuilder] that will decrypt a
+/// [CipherText] using a [Cipher].
+/// {@endtemplate}
+class DecryptionBuilder extends StatelessWidget {
+ /// {@macro decryption_builder}
const DecryptionBuilder({
- super.key,
required this.cipher,
- required this.data,
+ required this.cipherText,
required this.onLoading,
required this.onError,
required this.onSuccess,
+ super.key,
});
+ /// The [Cipher] that will be used to decrypt the [CipherText].
+ final Cipher cipher;
+
+ /// The [CipherText] that will be decrypted.
+ final CipherText cipherText;
+
+ /// The [Widget] that will be displayed while the [CipherText] is being
+ /// decrypted.
+ final Widget Function(BuildContext context) onLoading;
+
+ /// The [Widget] that will be displayed if an error occurs while decrypting
+ /// the [CipherText].
+ final Widget Function(BuildContext context, Object error) onError;
+
+ /// The [Widget] that will be displayed once the [CipherText] has been
+ /// decrypted.
+ final Widget Function(BuildContext context, Uint8List plainText) onSuccess;
+
@override
- Widget build(BuildContext context) {
- return FutureBuilder(
- future: cipher.decrypt(data),
- builder: (context, snapshot) {
- if (snapshot.hasData) {
- return onSuccess(context, snapshot.data!);
- } else if (snapshot.hasError) {
- return onError(context, snapshot.error!);
- }
- return onLoading(context);
- },
- );
- }
+ Widget build(BuildContext context) => FutureBuilder(
+ future: cipher.decrypt(cipherText),
+ builder: (context, snapshot) {
+ if (snapshot.hasData) {
+ return onSuccess(context, snapshot.data!);
+ } else if (snapshot.hasError) {
+ return onError(context, snapshot.error!);
+ }
+ return onLoading(context);
+ },
+ );
}
diff --git a/packages/native_crypto/lib/src/builders/encryption_builder.dart b/packages/native_crypto/lib/src/builders/encryption_builder.dart
new file mode 100644
index 0000000..5782730
--- /dev/null
+++ b/packages/native_crypto/lib/src/builders/encryption_builder.dart
@@ -0,0 +1,61 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:native_crypto/src/core/utils/cipher_text.dart';
+
+import 'package:native_crypto/src/domain/cipher.dart';
+import 'package:native_crypto/src/domain/cipher_chunk.dart';
+
+/// {@template encryption_builder}
+/// A [StatelessWidget] that builds a [FutureBuilder] that will
+/// encrypt a [Uint8List] using a [Cipher].
+/// {@endtemplate}
+class EncryptionBuilder extends StatelessWidget {
+ /// {@macro encryption_builder}
+ const EncryptionBuilder({
+ required this.cipher,
+ required this.plainText,
+ required this.onLoading,
+ required this.onError,
+ required this.onSuccess,
+ super.key,
+ });
+
+ /// The [Cipher] that will be used to encrypt the [Uint8List].
+ final Cipher cipher;
+
+ /// The [Uint8List] that will be encrypted.
+ final Uint8List plainText;
+
+ /// The [Widget] that will be displayed while the [Uint8List] is being
+ /// encrypted.
+ final Widget Function(BuildContext context) onLoading;
+
+ /// The [Widget] that will be displayed if an error occurs while encrypting
+ /// the [Uint8List].
+ final Widget Function(BuildContext context, Object error) onError;
+
+ /// The [Widget] that will be displayed once the [Uint8List] has been
+ /// encrypted.
+ final Widget Function(BuildContext context, CipherText cipherText)
+ onSuccess;
+
+ @override
+ Widget build(BuildContext context) => FutureBuilder>(
+ future: cipher.encrypt(plainText),
+ builder: (context, snapshot) {
+ if (snapshot.hasData) {
+ return onSuccess(context, snapshot.data!);
+ } else if (snapshot.hasError) {
+ return onError(context, snapshot.error!);
+ }
+ return onLoading(context);
+ },
+ );
+}
diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes.dart b/packages/native_crypto/lib/src/ciphers/aes/aes.dart
index b1113fc..9d995fe 100644
--- a/packages/native_crypto/lib/src/ciphers/aes/aes.dart
+++ b/packages/native_crypto/lib/src/ciphers/aes/aes.dart
@@ -1,181 +1,293 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: aes.dart
-// Created Date: 16/12/2021 16:28:00
-// Last Modified: 27/05/2022 12:13:28
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+import 'dart:io';
import 'dart:typed_data';
+import 'package:native_crypto/src/ciphers/aes/aes_cipher_chunk.dart';
import 'package:native_crypto/src/ciphers/aes/aes_key_size.dart';
import 'package:native_crypto/src/ciphers/aes/aes_mode.dart';
import 'package:native_crypto/src/ciphers/aes/aes_padding.dart';
-import 'package:native_crypto/src/core/cipher_text.dart';
-import 'package:native_crypto/src/core/cipher_text_wrapper.dart';
-import 'package:native_crypto/src/interfaces/cipher.dart';
+import 'package:native_crypto/src/core/constants/constants.dart';
+import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart';
+import 'package:native_crypto/src/core/utils/cipher_text.dart';
+import 'package:native_crypto/src/core/utils/platform.dart';
+import 'package:native_crypto/src/domain/cipher.dart';
import 'package:native_crypto/src/keys/secret_key.dart';
-import 'package:native_crypto/src/platform.dart';
-import 'package:native_crypto/src/utils/cipher_algorithm.dart';
-import 'package:native_crypto/src/utils/extensions.dart';
import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
+export 'aes_cipher_chunk.dart';
export 'aes_key_size.dart';
export 'aes_mode.dart';
export 'aes_padding.dart';
-/// An AES cipher.
+/// {@template aes}
+/// AES cipher.
///
-/// [AES] is a [Cipher] that can be used to encrypt or decrypt data.
-class AES implements Cipher {
- final SecretKey _key;
+/// [AES] is a symmetric cipher which means that the same key is used to encrypt
+/// and decrypt the data.
+/// {@endtemplate}
+class AES implements Cipher {
+ const AES({
+ required this.key,
+ required this.mode,
+ required this.padding,
+ this.chunkSize = Constants.defaultChunkSize,
+ });
+
+ static const String _algorithm = 'aes';
+
+ /// The key used to encrypt and decrypt the data.
+ final SecretKey key;
+
+ /// The [AESMode] used by this [AES].
final AESMode mode;
+
+ /// The [AESPadding] used by this [AES].
final AESPadding padding;
- @override
- CipherAlgorithm get algorithm => CipherAlgorithm.aes;
+ /// The size of the cipher text chunks.
+ final int chunkSize;
- AES(SecretKey key, [this.mode = AESMode.gcm, this.padding = AESPadding.none])
- : _key = key {
- if (!AESKeySize.supportedSizes.contains(key.bitLength)) {
- throw NativeCryptoException(
- message: 'Invalid key size! '
- 'Expected: ${AESKeySize.supportedSizes.join(', ')} bits',
- code: NativeCryptoExceptionCode.invalid_key_length.code,
+ @override
+ Future decrypt(CipherText cipherText) async {
+ final BytesBuilder plainText = BytesBuilder(copy: false);
+ final chunks = cipherText.chunks;
+
+ int i = 0;
+ for (final chunk in chunks) {
+ plainText.add(await _decryptChunk(chunk.bytes, count: i++));
+ }
+
+ return plainText.toBytes();
+ }
+
+ @override
+ Future decryptFile(File cipherTextFile, File plainTextFile) {
+ if (!cipherTextFile.existsSync()) {
+ throw ArgumentError.value(
+ cipherTextFile.path,
+ 'cipherTextFile.path',
+ 'File does not exist!',
+ );
+ }
+
+ if (plainTextFile.existsSync()) {
+ throw ArgumentError.value(
+ plainTextFile.path,
+ 'plainTextFile.path',
+ 'File already exists!',
+ );
+ }
+
+ return platform.decryptFile(
+ cipherTextPath: cipherTextFile.path,
+ plainTextPath: plainTextFile.path,
+ key: key.bytes,
+ algorithm: _algorithm,
+ );
+ }
+
+ @override
+ Future> encrypt(Uint8List plainText) async {
+ final chunks = [];
+ final chunkedPlainText = plainText.chunked(chunkSize);
+
+ int i = 0;
+ for (final plainTextChunk in chunkedPlainText) {
+ final bytes = await _encryptChunk(plainTextChunk, count: i++);
+ chunks.add(
+ AESCipherChunk(
+ bytes,
+ ivLength: mode.ivLength,
+ tagLength: mode.tagLength,
+ ),
+ );
+ }
+
+ return CipherText.fromChunks(
+ chunks,
+ chunkFactory: (bytes) => AESCipherChunk(
+ bytes,
+ ivLength: mode.ivLength,
+ tagLength: mode.tagLength,
+ ),
+ chunkSize: chunkSize,
+ );
+ }
+
+ @override
+ Future encryptFile(File plainTextFile, File cipherTextFile) {
+ if (!plainTextFile.existsSync()) {
+ throw ArgumentError.value(
+ plainTextFile.path,
+ 'plainTextFile.path',
+ 'File does not exist!',
+ );
+ }
+
+ if (cipherTextFile.existsSync()) {
+ throw ArgumentError.value(
+ cipherTextFile.path,
+ 'cipherTextFile.path',
+ 'File already exists!',
+ );
+ }
+
+ return platform.encryptFile(
+ plainTextPath: plainTextFile.path,
+ cipherTextPath: cipherTextFile.path,
+ key: key.bytes,
+ algorithm: _algorithm,
+ );
+ }
+
+ /// Encrypts the [plainText] with the [iv] chosen by the Flutter side.
+ ///
+ /// Prefer using [encrypt] instead which will generate a
+ /// random [iv] on the native side.
+ ///
+ /// Note: this method doesn't chunk the data. It can lead to memory issues
+ /// if the [plainText] is too big. Use [encrypt] instead.
+ Future encryptWithIV(
+ Uint8List plainText,
+ Uint8List iv,
+ ) async {
+ // Check if the cipher is correctly initialized
+ _isCorrectlyInitialized();
+
+ if (iv.length != mode.ivLength) {
+ throw ArgumentError.value(
+ iv.length,
+ 'iv.length',
+ 'Invalid iv length! '
+ 'Expected: ${mode.ivLength}',
+ );
+ }
+
+ final bytes = await platform.encryptWithIV(
+ plainText: plainText,
+ iv: iv,
+ key: key.bytes,
+ algorithm: _algorithm,
+ );
+
+ // TODO(hpcl): move these checks to the platform interface
+ if (bytes == null) {
+ throw const NativeCryptoException(
+ code: NativeCryptoExceptionCode.nullError,
+ message: 'Platform returned null bytes',
+ );
+ }
+
+ if (bytes.isEmpty) {
+ throw const NativeCryptoException(
+ code: NativeCryptoExceptionCode.invalidData,
+ message: 'Platform returned no data',
+ );
+ }
+
+ return AESCipherChunk(
+ bytes,
+ ivLength: mode.ivLength,
+ tagLength: mode.tagLength,
+ );
+ }
+
+ /// Ensures that the cipher is correctly initialized.
+ bool _isCorrectlyInitialized() {
+ final keySize = key.length * 8;
+ if (!AESKeySize.supportedSizes.contains(keySize)) {
+ throw ArgumentError.value(
+ keySize,
+ 'keySize',
+ 'Invalid key size! '
+ 'Expected: ${AESKeySize.supportedSizes.join(', ')}',
);
}
if (!mode.supportedPaddings.contains(padding)) {
- throw NativeCryptoException(
- message: 'Invalid padding! '
+ throw ArgumentError.value(
+ padding,
+ 'padding',
+ 'Invalid padding! '
'Expected: ${mode.supportedPaddings.join(', ')}',
- code: NativeCryptoExceptionCode.invalid_padding.code,
);
}
+
+ return true;
}
- Future _decrypt(
- CipherText cipherText, {
- int chunkCount = 0,
+ /// Encrypts the plain text chunk.
+ Future _encryptChunk(Uint8List plainChunk, {int count = 0}) async {
+ // Check if the cipher is correctly initialized
+ _isCorrectlyInitialized();
+
+ Uint8List? bytes;
+
+ try {
+ bytes = await platform.encrypt(
+ plainChunk,
+ key: key.bytes,
+ algorithm: _algorithm,
+ );
+ } on NativeCryptoException catch (e) {
+ throw e.copyWith(
+ message: 'Failed to encrypt chunk #$count: ${e.message}',
+ );
+ }
+
+ // TODO(hpcl): move these checks to the platform interface
+ if (bytes == null) {
+ throw NativeCryptoException(
+ code: NativeCryptoExceptionCode.nullError,
+ message: 'Platform returned null bytes on chunk #$count',
+ );
+ }
+
+ if (bytes.isEmpty) {
+ throw NativeCryptoException(
+ code: NativeCryptoExceptionCode.invalidData,
+ message: 'Platform returned no data on chunk #$count',
+ );
+ }
+
+ return bytes;
+ }
+
+ /// Decrypts the cipher text chunk.
+ Future _decryptChunk(
+ Uint8List cipherChunk, {
+ int count = 0,
}) async {
- Uint8List? decrypted;
+ // Check if the cipher is correctly initialized
+ _isCorrectlyInitialized();
+
+ Uint8List? bytes;
try {
- decrypted = await platform.decrypt(
- cipherText.bytes,
- _key.bytes,
- algorithm.name,
+ bytes = await platform.decrypt(
+ cipherChunk,
+ key: key.bytes,
+ algorithm: _algorithm,
);
- } catch (e, s) {
+ } on NativeCryptoException catch (e) {
+ throw e.copyWith(
+ message: 'Failed to decrypt chunk #$count: ${e.message}',
+ );
+ }
+
+ // TODO(hpcl): move these checks to the platform interface
+ if (bytes == null) {
throw NativeCryptoException(
- message: '$e',
- code: NativeCryptoExceptionCode.platform_throws.code,
- stackTrace: s,
+ code: NativeCryptoExceptionCode.nullError,
+ message: 'Platform returned null bytes on chunk #$count',
);
}
- if (decrypted.isNull) {
- throw NativeCryptoException(
- message: 'Platform returned null when decrypting chunk #$chunkCount',
- code: NativeCryptoExceptionCode.platform_returned_null.code,
- );
- } else if (decrypted!.isEmpty) {
- throw NativeCryptoException(
- message: 'Platform returned no data when decrypting chunk #$chunkCount',
- code: NativeCryptoExceptionCode.platform_returned_empty_data.code,
- );
- } else {
- return decrypted;
- }
- }
-
- Future _encrypt(Uint8List data, {int chunkCount = 0}) async {
- Uint8List? encrypted;
-
- try {
- encrypted = await platform.encrypt(
- data,
- _key.bytes,
- algorithm.name,
- );
- } catch (e, s) {
- throw NativeCryptoException(
- message: '$e on chunk #$chunkCount',
- code: NativeCryptoExceptionCode.platform_throws.code,
- stackTrace: s,
- );
- }
-
- if (encrypted.isNull) {
- throw NativeCryptoException(
- message: 'Platform returned null when encrypting chunk #$chunkCount',
- code: NativeCryptoExceptionCode.platform_returned_null.code,
- );
- } else if (encrypted!.isEmpty) {
- throw NativeCryptoException(
- message: 'Platform returned no data when encrypting chunk #$chunkCount',
- code: NativeCryptoExceptionCode.platform_returned_empty_data.code,
- );
- } else {
- try {
- return CipherText.fromBytes(
- encrypted,
- ivLength: 12,
- messageLength: encrypted.length - 28,
- tagLength: 16,
- cipherAlgorithm: CipherAlgorithm.aes,
- );
- } on NativeCryptoException catch (e, s) {
- throw NativeCryptoException(
- message: '${e.message} on chunk #$chunkCount',
- code: e.code,
- stackTrace: s,
- );
- }
- }
- }
-
- @override
- Future decrypt(CipherTextWrapper cipherText) async {
- final BytesBuilder decryptedData = BytesBuilder(copy: false);
-
- if (cipherText.isList) {
- int chunkCount = 0;
- for (final CipherText chunk in cipherText.list) {
- decryptedData.add(await _decrypt(chunk, chunkCount: chunkCount++));
- }
- } else {
- decryptedData.add(await _decrypt(cipherText.single));
- }
-
- return decryptedData.toBytes();
- }
-
- @override
- Future encrypt(Uint8List data) async {
- if (data.isEmpty) {
- return CipherTextWrapper.empty();
- }
- CipherTextWrapper cipherTextWrapper;
- Uint8List dataToEncrypt;
- final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil();
-
- if (chunkNb > 1) {
- cipherTextWrapper = CipherTextWrapper.empty();
- for (var i = 0; i < chunkNb; i++) {
- dataToEncrypt = i < (chunkNb - 1)
- ? data.sublist(
- i * Cipher.bytesCountPerChunk,
- (i + 1) * Cipher.bytesCountPerChunk,
- )
- : data.sublist(i * Cipher.bytesCountPerChunk);
- cipherTextWrapper.add(await _encrypt(dataToEncrypt, chunkCount: i));
- }
- } else {
- cipherTextWrapper = CipherTextWrapper.single(await _encrypt(data));
- }
-
- return cipherTextWrapper;
+ return bytes;
}
}
diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart
new file mode 100644
index 0000000..7f8bb45
--- /dev/null
+++ b/packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart
@@ -0,0 +1,67 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/domain/cipher_chunk.dart';
+
+class AESCipherChunk extends CipherChunk {
+ const AESCipherChunk(
+ super.bytes, {
+ required this.ivLength,
+ required this.tagLength,
+ });
+
+ /// Creates a [AESCipherChunk] from a [List].
+ AESCipherChunk.fromList(
+ super.list, {
+ required this.ivLength,
+ required this.tagLength,
+ }) : super.fromList();
+
+ /// Creates a [AESCipherChunk] from a [String] encoded in UTF-8.
+ AESCipherChunk.fromUtf8(
+ super.encoded, {
+ required this.ivLength,
+ required this.tagLength,
+ }) : super.fromUtf8();
+
+ /// Creates a [AESCipherChunk] from a [String] encoded in UTF-16.
+ AESCipherChunk.fromUtf16(
+ super.encoded, {
+ required this.ivLength,
+ required this.tagLength,
+ }) : super.fromUtf16();
+
+ /// Creates a [AESCipherChunk] from a [String] encoded in Hexadecimal.
+ AESCipherChunk.fromBase16(
+ super.encoded, {
+ required this.ivLength,
+ required this.tagLength,
+ }) : super.fromBase16();
+
+ /// Creates a [AESCipherChunk] from a [String] encoded in Base64.
+ AESCipherChunk.fromBase64(
+ super.encoded, {
+ required this.ivLength,
+ required this.tagLength,
+ }) : super.fromBase64();
+
+ /// Intialization vector length.
+ final int ivLength;
+
+ /// Tag length.
+ final int tagLength;
+
+ /// Returns the initialization vector, or nonce of the [AESCipherChunk].
+ Uint8List get iv => bytes.sublist(0, ivLength);
+
+ /// Returns the tag of the [AESCipherChunk].
+ Uint8List get tag => bytes.sublist(bytes.length - tagLength, bytes.length);
+
+ /// Returns the message of the [AESCipherChunk].
+ Uint8List get message => bytes.sublist(ivLength, bytes.length - tagLength);
+}
diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart
index befa22f..72e81d7 100644
--- a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart
+++ b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart
@@ -1,18 +1,20 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: aes_key_size.dart
-// Created Date: 23/05/2022 22:10:07
-// Last Modified: 26/05/2022 18:45:01
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
-/// Defines all available key sizes.
+/// {@template aes_key_size}
+/// Defines the key size of an AES cipher.
+/// {@endtemplate}
enum AESKeySize {
bits128(128),
bits192(192),
bits256(256);
+ /// {@macro aes_key_size}
+ const AESKeySize(this.bits);
+
/// Returns the number of bits supported by an [AESKeySize].
static final List supportedSizes = [128, 192, 256];
@@ -21,6 +23,4 @@ enum AESKeySize {
/// Returns the number of bytes in this [AESKeySize].
int get bytes => bits ~/ 8;
-
- const AESKeySize(this.bits);
}
diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart
index 4bbc7c4..9e34304 100644
--- a/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart
+++ b/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart
@@ -1,17 +1,29 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: aes_mode.dart
-// Created Date: 23/05/2022 22:09:16
-// Last Modified: 26/05/2022 21:03:26
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
import 'package:native_crypto/src/ciphers/aes/aes_padding.dart';
+import 'package:native_crypto/src/core/constants/constants.dart';
+/// {@template aes_mode}
/// Defines the AES modes of operation.
+/// {@endtemplate}
enum AESMode {
- gcm([AESPadding.none], 12, 16);
+ /// GCM mode.
+ gcm(
+ [AESPadding.none],
+ Constants.aesGcmNonceLength,
+ Constants.aesGcmTagLength,
+ );
+
+ /// {@macro aes_mode}
+ const AESMode(
+ this.supportedPaddings, [
+ this.ivLength = 16,
+ this.tagLength = 0,
+ ]);
/// Returns the list of supported [AESPadding] for this [AESMode].
final List supportedPaddings;
@@ -21,10 +33,4 @@ enum AESMode {
/// Returns the default tag length for this [AESMode].
final int tagLength;
-
- const AESMode(
- this.supportedPaddings, [
- this.ivLength = 16,
- this.tagLength = 0,
- ]);
}
diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart
index 343ae03..0d26a9f 100644
--- a/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart
+++ b/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart
@@ -1,11 +1,11 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: aes_padding.dart
-// Created Date: 23/05/2022 22:10:17
-// Last Modified: 25/05/2022 09:23:49
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
-/// Represents different paddings.
-enum AESPadding { none }
+/// Padding used for AES encryption.
+enum AESPadding {
+ /// No padding.
+ none,
+}
diff --git a/packages/native_crypto/lib/src/ciphers/ciphers.dart b/packages/native_crypto/lib/src/ciphers/ciphers.dart
index edae6a4..2b87da6 100644
--- a/packages/native_crypto/lib/src/ciphers/ciphers.dart
+++ b/packages/native_crypto/lib/src/ciphers/ciphers.dart
@@ -1,10 +1,7 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: ciphers.dart
-// Created Date: 23/05/2022 22:56:30
-// Last Modified: 23/05/2022 22:56:47
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
export 'aes/aes.dart';
diff --git a/packages/native_crypto/lib/src/core/cipher_text.dart b/packages/native_crypto/lib/src/core/cipher_text.dart
deleted file mode 100644
index 6accd59..0000000
--- a/packages/native_crypto/lib/src/core/cipher_text.dart
+++ /dev/null
@@ -1,117 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: cipher_text.dart
-// Created Date: 16/12/2021 16:59:53
-// Last Modified: 27/05/2022 12:09:47
-// -----
-// Copyright (c) 2021
-
-import 'dart:typed_data';
-
-import 'package:native_crypto/src/core/cipher_text_wrapper.dart';
-import 'package:native_crypto/src/interfaces/byte_array.dart';
-import 'package:native_crypto/src/interfaces/cipher.dart';
-import 'package:native_crypto/src/utils/cipher_algorithm.dart';
-import 'package:native_crypto/src/utils/extensions.dart';
-import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
-
-/// Represents a cipher text in NativeCrypto.
-///
-/// [CipherText] is a [ByteArray] that can be used to store encrypted data.
-/// It is represented like:
-/// ```txt
-/// [IV + MESSAGE + TAG]
-/// ```
-/// where:
-/// - IV's length is [CipherText.ivLength] bytes.
-/// - MESSAGE's length is [CipherText.messageLength] bytes.
-/// - TAG's length is [CipherText.tagLength] bytes.
-///
-/// Check [CipherTextWrapper] for more information.
-class CipherText extends ByteArray {
- final int _ivLength;
- final int _messageLength;
- final int _tagLength;
-
- final CipherAlgorithm? _cipherAlgorithm;
-
- const CipherText._(
- this._ivLength,
- this._messageLength,
- this._tagLength,
- this._cipherAlgorithm,
- super.bytes,
- );
-
- factory CipherText.fromBytes(
- Uint8List bytes, {
- required int ivLength,
- required int tagLength,
- int? messageLength,
- CipherAlgorithm? cipherAlgorithm,
- }) {
- messageLength ??= bytes.length - ivLength - tagLength;
-
- if (ivLength.isNegative ||
- messageLength.isNegative ||
- tagLength.isNegative) {
- throw NativeCryptoException(
- message: 'Invalid length! Must be positive.',
- code: NativeCryptoExceptionCode.invalid_argument.code,
- );
- }
-
- if (bytes.isEmpty) {
- throw NativeCryptoException(
- message: 'Passed data is empty!',
- code: NativeCryptoExceptionCode.invalid_argument.code,
- );
- }
-
- if (bytes.length != ivLength + messageLength + tagLength) {
- throw NativeCryptoException(
- message: 'Invalid cipher text length! '
- 'Expected: ${ivLength + messageLength + tagLength} bytes '
- 'got: ${bytes.length} bytes.',
- code: NativeCryptoExceptionCode.invalid_argument.code,
- );
- }
-
- if (messageLength > Cipher.bytesCountPerChunk) {
- throw NativeCryptoException(
- message: 'Cipher text is too big! Consider using chunks.',
- code: NativeCryptoExceptionCode.invalid_argument.code,
- );
- }
-
- return CipherText._(
- ivLength,
- messageLength,
- tagLength,
- cipherAlgorithm,
- bytes,
- );
- }
-
- /// Gets the [CipherAlgorithm] used to encrypt the [CipherText].
- CipherAlgorithm get cipherAlgorithm {
- if (_cipherAlgorithm.isNotNull) {
- return _cipherAlgorithm!;
- } else {
- throw NativeCryptoException(
- message: 'Cipher algorithm is not specified',
- code: NativeCryptoExceptionCode.invalid_cipher.code,
- );
- }
- }
-
- /// Gets the length of the [CipherText]'s IV.
- int get ivLength => _ivLength;
-
- /// Gets the length of the [CipherText]'s Message.
- int get messageLength => _messageLength;
-
- /// Gets the length of the [CipherText]'s Tag.
- int get tagLength => _tagLength;
-}
diff --git a/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart b/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart
deleted file mode 100644
index 75a9dae..0000000
--- a/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: cipher_text_wrapper.dart
-// Created Date: 26/05/2022 14:27:32
-// Last Modified: 27/05/2022 13:43:29
-// -----
-// Copyright (c) 2022
-
-import 'dart:typed_data';
-
-import 'package:native_crypto/native_crypto.dart';
-import 'package:native_crypto/src/utils/extensions.dart';
-
-/// Wrapper for [CipherText]
-///
-/// Typically, this object is the result of an encryption operation.
-/// For decryption you have to build this before using it.
-class CipherTextWrapper {
- final CipherText? _single;
- final List? _list;
-
- CipherTextWrapper._(this._single, this._list);
-
- /// Creates a [CipherTextWrapper] from a [CipherText].
- factory CipherTextWrapper.single(CipherText cipherText) =>
- CipherTextWrapper._(cipherText, null);
-
- /// Creates a [CipherTextWrapper] from a [List] of [CipherText].
- factory CipherTextWrapper.list(List cipherTexts) =>
- CipherTextWrapper._(null, cipherTexts);
-
- /// Creates an empty [List] in a [CipherTextWrapper].
- ///
- /// This is useful when you want to create a [CipherTextWrapper] then
- /// fill it with data.
- factory CipherTextWrapper.empty() => CipherTextWrapper._(null, []);
-
- /// Creates a [CipherTextWrapper] from a [Uint8List].
- ///
- /// This is a convenience method to create a [CipherTextWrapper]
- /// from a [Uint8List]. It tries to detect if the [Uint8List] is a
- /// single [CipherText] or a list of [CipherText].
- ///
- /// You can customize the chunk size by passing a [chunkSize] parameter.
- /// The default chunk size is [Cipher.bytesCountPerChunk].
- ///
- /// Throw an [NativeCryptoExceptionCode] with
- /// [NativeCryptoExceptionCode.invalid_argument] if the [Uint8List] is
- /// not a valid [CipherText] or a [List] of [CipherText].
- factory CipherTextWrapper.fromBytes(
- Uint8List bytes, {
- required int ivLength,
- required int tagLength,
- CipherAlgorithm? cipherAlgorithm,
- int? chunkSize,
- }) {
- chunkSize ??= Cipher.bytesCountPerChunk;
- Cipher.bytesCountPerChunk = chunkSize;
-
- final int messageLength = bytes.length - ivLength - tagLength;
-
- if (messageLength <= chunkSize) {
- return CipherTextWrapper.single(
- CipherText.fromBytes(
- bytes,
- ivLength: ivLength,
- tagLength: tagLength,
- cipherAlgorithm: cipherAlgorithm,
- ),
- );
- } else {
- final cipherTexts = [];
- for (var i = 0; i < bytes.length; i += chunkSize + ivLength + tagLength) {
- final chunk = bytes.trySublist(i, i + chunkSize + ivLength + tagLength);
-
- try {
- cipherTexts.add(
- CipherText.fromBytes(
- chunk,
- ivLength: ivLength,
- tagLength: tagLength,
- cipherAlgorithm: cipherAlgorithm,
- ),
- );
- } on NativeCryptoException catch (e, s) {
- throw NativeCryptoException(
- message: '${e.message} on chunk #$i',
- code: e.code,
- stackTrace: s,
- );
- }
- }
- return CipherTextWrapper.list(cipherTexts);
- }
- }
-
- /// Checks if the [CipherText] is a single [CipherText].
- bool get isSingle => _single.isNotNull;
-
- /// Checks if the [CipherText] is a [List] of [CipherText].
- bool get isList => _list.isNotNull;
-
- /// Gets the [CipherText] if it's a single one.
- ///
- /// Throws [NativeCryptoException] with
- /// [NativeCryptoExceptionCode.invalid_data] if it's not a single one.
- CipherText get single {
- if (isSingle) {
- return _single!;
- } else {
- throw NativeCryptoException(
- message: 'CipherTextWrapper is not single',
- code: NativeCryptoExceptionCode.invalid_data.code,
- );
- }
- }
-
- /// Gets the [List] of [CipherText] if it's a list.
- ///
- /// Throws [NativeCryptoException] with
- /// [NativeCryptoExceptionCode.invalid_data] if it's not a list.
- List get list {
- if (isList) {
- return _list!;
- } else {
- throw NativeCryptoException(
- message: 'CipherTextWrapper is not list',
- code: NativeCryptoExceptionCode.invalid_data.code,
- );
- }
- }
-
- /// Gets the raw [Uint8List] of the [CipherText] or [List] of [CipherText].
- Uint8List get bytes {
- if (isSingle) {
- return single.bytes;
- } else {
- return list.map((cipherText) => cipherText.bytes).toList().combine();
- }
- }
-
- /// Gets the number of parts of the [CipherText] or [List] of [CipherText].
- ///
- /// Check [Cipher.bytesCountPerChunk] for more information.
- int get chunkCount {
- _single.isNull;
- if (_single.isNotNull) {
- return 1;
- } else {
- return _list?.length ?? 0;
- }
- }
-
- /// Gets the [CipherText] or the [List] of [CipherText].
- ///
- /// Throws [NativeCryptoException] with
- /// [NativeCryptoExceptionCode.invalid_data] if it's not a single or a list or
- /// if [T] is not [CipherText] or [List] of [CipherText].
- T unwrap() {
- if (isSingle && T == CipherText) {
- return single as T;
- } else if (isList && T == List) {
- return list as T;
- } else {
- final String type =
- isSingle ? 'CipherText' : (isList ? 'List' : 'unknown');
- throw NativeCryptoException(
- message: 'CipherTextWrapper is not a $T but a $type, '
- 'you should use unwrap<$type>()',
- code: NativeCryptoExceptionCode.invalid_data.code,
- );
- }
- }
-
- void add(CipherText cipherText) {
- if (isSingle) {
- throw NativeCryptoException(
- message: 'CipherTextWrapper is already single',
- code: NativeCryptoExceptionCode.invalid_data.code,
- );
- } else if (isList) {
- _list!.add(cipherText);
- } else {
- throw NativeCryptoException(
- message: 'CipherTextWrapper is not single or list',
- code: NativeCryptoExceptionCode.invalid_data.code,
- );
- }
- }
-}
diff --git a/packages/native_crypto/lib/src/core/constants/constants.dart b/packages/native_crypto/lib/src/core/constants/constants.dart
new file mode 100644
index 0000000..37fe0b5
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/constants/constants.dart
@@ -0,0 +1,18 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+abstract class Constants {
+ /// The default chunk size in bytes used for encryption and decryption.
+ ///
+ /// ~32MB
+ static const int defaultChunkSize = 33554432;
+
+ /// The length of the initialization vector in bytes used for AES GCM.
+ static const int aesGcmNonceLength = 12;
+
+ /// The length of the tag in bytes used for AES GCM.
+ static const int aesGcmTagLength = 16;
+}
diff --git a/packages/native_crypto/lib/src/core/core.dart b/packages/native_crypto/lib/src/core/core.dart
index 32ad783..7a26b2a 100644
--- a/packages/native_crypto/lib/src/core/core.dart
+++ b/packages/native_crypto/lib/src/core/core.dart
@@ -1,11 +1,11 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: core.dart
-// Created Date: 23/05/2022 23:05:26
-// Last Modified: 26/05/2022 17:10:25
-// -----
-// Copyright (c) 2022
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
-export 'cipher_text.dart';
-export 'cipher_text_wrapper.dart';
+export './constants/constants.dart';
+export './enums/enums.dart';
+export './extensions/extensions.dart';
+export './utils/utils.dart';
+export 'utils/platform.dart';
diff --git a/packages/native_crypto/lib/src/core/enums/encoding.dart b/packages/native_crypto/lib/src/core/enums/encoding.dart
new file mode 100644
index 0000000..fb459dd
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/enums/encoding.dart
@@ -0,0 +1,20 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+/// An encoding used to convert a byte array to a string and vice-versa.
+enum Encoding {
+ /// UTF-8 encoding, as defined by the Unicode standard.
+ utf8,
+
+ /// UTF-16 encoding, as defined by the Unicode standard.
+ utf16,
+
+ /// Base64 encoding, as defined by RFC 4648.
+ base64,
+
+ /// Hexadecimal encoding.
+ base16,
+}
diff --git a/packages/native_crypto/lib/src/core/enums/enums.dart b/packages/native_crypto/lib/src/core/enums/enums.dart
new file mode 100644
index 0000000..bc34b6b
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/enums/enums.dart
@@ -0,0 +1,8 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+export 'encoding.dart';
+export 'hash_algorithm.dart';
diff --git a/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart
new file mode 100644
index 0000000..860f609
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart
@@ -0,0 +1,17 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+/// The hash algorithm to use in Message Digest and HMAC.
+enum HashAlgorithm {
+ /// The SHA-256 hash algorithm.
+ sha256,
+
+ /// The SHA-384 hash algorithm.
+ sha384,
+
+ /// The SHA-512 hash algorithm.
+ sha512,
+}
diff --git a/packages/native_crypto/lib/src/core/extensions/extensions.dart b/packages/native_crypto/lib/src/core/extensions/extensions.dart
new file mode 100644
index 0000000..811436f
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/extensions/extensions.dart
@@ -0,0 +1,10 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+export 'list_int_extension.dart';
+export 'list_uint8_list_extension.dart';
+export 'string_extension.dart';
+export 'uint8_list_extension.dart';
diff --git a/packages/native_crypto/lib/src/core/extensions/list_int_extension.dart b/packages/native_crypto/lib/src/core/extensions/list_int_extension.dart
new file mode 100644
index 0000000..5ae7252
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/extensions/list_int_extension.dart
@@ -0,0 +1,12 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+extension ListIntExtension on List {
+ /// Converts a [List] of int to a [Uint8List].
+ Uint8List toTypedList() => Uint8List.fromList(this);
+}
diff --git a/packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart b/packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart
new file mode 100644
index 0000000..78dfe75
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart
@@ -0,0 +1,19 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart';
+
+extension ListUint8ListExtension on List {
+ /// Reduce a [List] of [Uint8List] to a [Uint8List].
+ Uint8List combine() {
+ if (isEmpty) {
+ return Uint8List(0);
+ }
+ return reduce((value, element) => value | element);
+ }
+}
diff --git a/packages/native_crypto/lib/src/core/extensions/string_extension.dart b/packages/native_crypto/lib/src/core/extensions/string_extension.dart
new file mode 100644
index 0000000..3a342b7
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/extensions/string_extension.dart
@@ -0,0 +1,36 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/enums/encoding.dart';
+import 'package:native_crypto/src/core/extensions/list_int_extension.dart';
+
+extension StringExtension on String {
+ /// Converts a [String] to a [Uint8List] using the specified [Encoding].
+ Uint8List toBytes({Encoding from = Encoding.utf16}) {
+ Uint8List bytes;
+ switch (from) {
+ case Encoding.utf8:
+ bytes = utf8.encode(this).toTypedList();
+ break;
+ case Encoding.utf16:
+ bytes = runes.toList().toTypedList();
+ break;
+ case Encoding.base64:
+ bytes = base64.decode(this);
+ break;
+ case Encoding.base16:
+ assert(length.isEven, 'String needs to be an even length.');
+ bytes = List.generate(
+ length ~/ 2,
+ (i) => int.parse(substring(i * 2, (i * 2) + 2), radix: 16),
+ ).toList().toTypedList();
+ }
+ return bytes;
+ }
+}
diff --git a/packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart b/packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart
new file mode 100644
index 0000000..daf870a
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart
@@ -0,0 +1,69 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/enums/encoding.dart';
+import 'package:native_crypto/src/core/extensions/list_int_extension.dart';
+
+extension Uint8ListExtension on Uint8List {
+ /// Returns a concatenation of this with the other [Uint8List].
+ Uint8List plus(Uint8List other) => [...this, ...other].toTypedList();
+
+ /// Returns a concatenation of this with the other
+ /// [Uint8List] using the `|` operator as a shortcut.
+ Uint8List operator |(Uint8List other) => plus(other);
+
+ /// Returns a sublist of this from the [start] index to the [end] index.
+ /// If [end] is greater than the length of the list, it is set to the length.
+ Uint8List trySublist(int start, [int? end]) {
+ if (isEmpty) {
+ return this;
+ }
+
+ int ending = end ?? length;
+ if (ending > length) {
+ ending = length;
+ }
+
+ return sublist(start, ending);
+ }
+
+ /// Returns a [List] of [Uint8List] of the specified [chunkSize].
+ List chunked(int chunkSize) {
+ if (isEmpty) {
+ return [];
+ }
+
+ return List.generate(
+ (length / chunkSize).ceil(),
+ (i) => trySublist(i * chunkSize, (i * chunkSize) + chunkSize),
+ );
+ }
+
+ /// Converts a [Uint8List] to a [String] using the specified [Encoding].
+ String toStr({Encoding to = Encoding.utf16}) {
+ String str;
+ switch (to) {
+ case Encoding.utf8:
+ str = utf8.decode(this);
+ break;
+ case Encoding.utf16:
+ str = String.fromCharCodes(this);
+ break;
+ case Encoding.base64:
+ str = base64.encode(this);
+ break;
+ case Encoding.base16:
+ str = List.generate(
+ length,
+ (i) => this[i].toRadixString(16).padLeft(2, '0'),
+ ).join();
+ }
+ return str;
+ }
+}
diff --git a/packages/native_crypto/lib/src/core/utils/chunk_factory.dart b/packages/native_crypto/lib/src/core/utils/chunk_factory.dart
new file mode 100644
index 0000000..afbc6b4
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/utils/chunk_factory.dart
@@ -0,0 +1,12 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/domain/cipher_chunk.dart';
+
+/// A factory that creates a [CipherChunk] of type [T] from a [Uint8List].
+typedef ChunkFactory = T Function(Uint8List chunk);
diff --git a/packages/native_crypto/lib/src/core/utils/cipher_text.dart b/packages/native_crypto/lib/src/core/utils/cipher_text.dart
new file mode 100644
index 0000000..d9e6977
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/utils/cipher_text.dart
@@ -0,0 +1,47 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/constants/constants.dart';
+import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart';
+import 'package:native_crypto/src/core/utils/chunk_factory.dart';
+import 'package:native_crypto/src/domain/byte_array.dart';
+import 'package:native_crypto/src/domain/cipher_chunk.dart';
+
+/// {@template cipher_text}
+/// A [CipherText] is a [ByteArray] that is used to store a text encrypted by a
+/// Cipher.
+/// {@endtemplate}
+class CipherText extends ByteArray {
+ /// {@macro cipher_text}
+ CipherText(
+ super.bytes, {
+ required ChunkFactory this.chunkFactory,
+ this.chunkSize = Constants.defaultChunkSize,
+ }) : chunks = bytes.chunked(chunkSize).map(chunkFactory).toList();
+
+ /// Creates a [CipherText] from a [List] of [CipherChunk].
+ CipherText.fromChunks(
+ this.chunks, {
+ required ChunkFactory this.chunkFactory,
+ this.chunkSize = Constants.defaultChunkSize,
+ }) : super(
+ chunks.fold(
+ Uint8List(0),
+ (acc, chunk) => acc | chunk.bytes,
+ ),
+ );
+
+ /// Factory used to create [CipherChunk] from an Uint8List.
+ final ChunkFactory? chunkFactory;
+
+ /// List of [CipherChunk] that compose the [CipherText].
+ final List chunks;
+
+ /// Size of one chunk.
+ final int chunkSize;
+}
diff --git a/packages/native_crypto/lib/src/core/utils/platform.dart b/packages/native_crypto/lib/src/core/utils/platform.dart
new file mode 100644
index 0000000..0555237
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/utils/platform.dart
@@ -0,0 +1,9 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
+
+NativeCryptoPlatform platform = NativeCryptoPlatform.instance;
diff --git a/packages/native_crypto/lib/src/core/utils/utils.dart b/packages/native_crypto/lib/src/core/utils/utils.dart
new file mode 100644
index 0000000..40b6a15
--- /dev/null
+++ b/packages/native_crypto/lib/src/core/utils/utils.dart
@@ -0,0 +1,8 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+export 'chunk_factory.dart';
+export 'cipher_text.dart';
diff --git a/packages/native_crypto/lib/src/digest/digest.dart b/packages/native_crypto/lib/src/digest/digest.dart
new file mode 100644
index 0000000..d5ab308
--- /dev/null
+++ b/packages/native_crypto/lib/src/digest/digest.dart
@@ -0,0 +1,8 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+export 'hash.dart';
+export 'hmac.dart';
diff --git a/packages/native_crypto/lib/src/digest/hash.dart b/packages/native_crypto/lib/src/digest/hash.dart
new file mode 100644
index 0000000..30f33c7
--- /dev/null
+++ b/packages/native_crypto/lib/src/digest/hash.dart
@@ -0,0 +1,62 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/enums/hash_algorithm.dart';
+import 'package:native_crypto/src/core/utils/platform.dart';
+import 'package:native_crypto/src/domain/hash.dart';
+import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
+
+class _Hash extends Hash {
+ const _Hash(this.algorithm);
+
+ @override
+ Future digest(Uint8List data) async {
+ final hash = await platform.hash(data, algorithm: algorithm.name);
+
+ // TODO(hpcl): move these checks to the platform interface
+ if (hash == null) {
+ throw const NativeCryptoException(
+ code: NativeCryptoExceptionCode.nullError,
+ message: 'Platform returned null bytes',
+ );
+ }
+
+ if (hash.isEmpty) {
+ throw const NativeCryptoException(
+ code: NativeCryptoExceptionCode.invalidData,
+ message: 'Platform returned no data.',
+ );
+ }
+
+ return hash;
+ }
+
+ @override
+ final HashAlgorithm algorithm;
+}
+
+/// A [Hash] that uses the SHA-256 algorithm.
+class Sha256 extends _Hash {
+ factory Sha256() => _instance ??= Sha256._();
+ Sha256._() : super(HashAlgorithm.sha256);
+ static Sha256? _instance;
+}
+
+/// A [Hash] that uses the SHA-384 algorithm.
+class Sha384 extends _Hash {
+ factory Sha384() => _instance ??= Sha384._();
+ Sha384._() : super(HashAlgorithm.sha384);
+ static Sha384? _instance;
+}
+
+/// A [Hash] that uses the SHA-512 algorithm.
+class Sha512 extends _Hash {
+ factory Sha512() => _instance ??= Sha512._();
+ Sha512._() : super(HashAlgorithm.sha512);
+ static Sha512? _instance;
+}
diff --git a/packages/native_crypto/lib/src/digest/hmac.dart b/packages/native_crypto/lib/src/digest/hmac.dart
new file mode 100644
index 0000000..69419b0
--- /dev/null
+++ b/packages/native_crypto/lib/src/digest/hmac.dart
@@ -0,0 +1,67 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/enums/hash_algorithm.dart';
+import 'package:native_crypto/src/core/utils/platform.dart';
+import 'package:native_crypto/src/domain/hmac.dart';
+import 'package:native_crypto/src/keys/secret_key.dart';
+import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
+
+class _Hmac extends Hmac {
+ const _Hmac(this.algorithm);
+
+ @override
+ Future digest(Uint8List data, SecretKey key) async {
+ final hmac = await platform.hmac(
+ data,
+ key: key.bytes,
+ algorithm: algorithm.name,
+ );
+
+ // TODO(hpcl): move these checks to the platform interface
+ if (hmac == null) {
+ throw const NativeCryptoException(
+ code: NativeCryptoExceptionCode.nullError,
+ message: 'Platform returned null bytes',
+ );
+ }
+
+ if (hmac.isEmpty) {
+ throw const NativeCryptoException(
+ code: NativeCryptoExceptionCode.invalidData,
+ message: 'Platform returned no data.',
+ );
+ }
+
+ return hmac;
+ }
+
+ @override
+ final HashAlgorithm algorithm;
+}
+
+/// A [Hmac] that uses the SHA-256 algorithm.
+class HmacSha256 extends _Hmac {
+ factory HmacSha256() => _instance ??= HmacSha256._();
+ HmacSha256._() : super(HashAlgorithm.sha256);
+ static HmacSha256? _instance;
+}
+
+/// A [Hmac] that uses the SHA-384 algorithm.
+class HmacSha384 extends _Hmac {
+ factory HmacSha384() => _instance ??= HmacSha384._();
+ HmacSha384._() : super(HashAlgorithm.sha384);
+ static HmacSha384? _instance;
+}
+
+/// A [Hmac] that uses the SHA-512 algorithm.
+class HmacSha512 extends _Hmac {
+ factory HmacSha512() => _instance ??= HmacSha512._();
+ HmacSha512._() : super(HashAlgorithm.sha512);
+ static HmacSha512? _instance;
+}
diff --git a/packages/native_crypto/lib/src/domain/base_key.dart b/packages/native_crypto/lib/src/domain/base_key.dart
new file mode 100644
index 0000000..be6e135
--- /dev/null
+++ b/packages/native_crypto/lib/src/domain/base_key.dart
@@ -0,0 +1,37 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'package:native_crypto/src/domain/byte_array.dart';
+
+/// {@template base_key}
+/// Represents a key in NativeCrypto.
+///
+/// [BaseKey] is a [ByteArray] that can be used to store keys.
+///
+/// This interface is implemented by all the key classes.
+///
+/// Note: [BaseKey] is named [BaseKey] instead of Key to avoid conflicts with
+/// the Key class from Flutter.
+/// {@endtemplate}
+abstract class BaseKey extends ByteArray {
+ /// {@macro base_key}
+ const BaseKey(super.bytes);
+
+ /// Creates a [BaseKey] from a [List].
+ BaseKey.fromList(super.list) : super.fromList();
+
+ /// Creates a [BaseKey] from a [String] encoded in UTF-8.
+ BaseKey.fromUtf8(super.encoded) : super.fromUtf8();
+
+ /// Creates a [BaseKey] from a [String] encoded in UTF-16.
+ BaseKey.fromUtf16(super.encoded) : super.fromUtf16();
+
+ /// Creates a [BaseKey] from a [String] encoded in Hexadecimal.
+ BaseKey.fromBase16(super.encoded) : super.fromBase16();
+
+ /// Creates a [BaseKey] from a [String] encoded in Base64.
+ BaseKey.fromBase64(super.encoded) : super.fromBase64();
+}
diff --git a/packages/native_crypto/lib/src/interfaces/byte_array.dart b/packages/native_crypto/lib/src/domain/byte_array.dart
similarity index 61%
rename from packages/native_crypto/lib/src/interfaces/byte_array.dart
rename to packages/native_crypto/lib/src/domain/byte_array.dart
index 7cdef3a..46bae1c 100644
--- a/packages/native_crypto/lib/src/interfaces/byte_array.dart
+++ b/packages/native_crypto/lib/src/domain/byte_array.dart
@@ -1,48 +1,49 @@
-// Author: Hugo Pointcheval
-// Email: git@pcl.ovh
-// -----
-// File: byte_array.dart
-// Created Date: 16/12/2021 17:54:16
-// Last Modified: 26/05/2022 17:13:27
-// -----
-// Copyright (c) 2021
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
import 'dart:typed_data';
-import 'package:flutter/foundation.dart';
-import 'package:native_crypto/src/utils/encoding.dart';
-import 'package:native_crypto/src/utils/extensions.dart';
+import 'package:equatable/equatable.dart';
+import 'package:native_crypto/src/core/enums/encoding.dart';
+import 'package:native_crypto/src/core/extensions/list_int_extension.dart';
+import 'package:native_crypto/src/core/extensions/string_extension.dart';
+import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart';
+/// {@template byte_array}
/// Represents a byte array.
///
/// [ByteArray] wraps a [Uint8List] and provides some useful conversion methods.
-@immutable
-abstract class ByteArray {
- final Uint8List _bytes;
-
- /// Creates a [ByteArray] from a [Uint8List].
+/// {@endtemplate}
+abstract class ByteArray extends Equatable {
+ /// {@macro byte_array}
const ByteArray(this._bytes);
- /// Creates a [ByteArray] object from a hexdecimal string.
- ByteArray.fromBase16(String encoded)
- : _bytes = encoded.toBytes(from: Encoding.base16);
+ /// Creates a [ByteArray] object from a [List] of [int].
+ ByteArray.fromList(List list) : _bytes = list.toTypedList();
- /// Creates a [ByteArray] object from a Base64 string.
- ByteArray.fromBase64(String encoded)
- : _bytes = encoded.toBytes(from: Encoding.base64);
+ /// Creates an empty [ByteArray] object from a length.
+ ByteArray.fromLength(int length, {int fill = 0})
+ : _bytes = Uint8List(length)..fillRange(0, length, fill);
+
+ /// Creates a [ByteArray] object from an UTF-16 string.
+ ByteArray.fromUtf16(String encoded) : _bytes = encoded.toBytes();
/// Creates a [ByteArray] object from an UTF-8 string.
ByteArray.fromUtf8(String encoded)
: _bytes = encoded.toBytes(from: Encoding.utf8);
- /// Creates a [ByteArray] object from an UTF-16 string.
- ByteArray.fromUtf16(String encoded) : _bytes = encoded.toBytes();
+ /// Creates a [ByteArray] object from a Base64 string.
+ ByteArray.fromBase64(String encoded)
+ : _bytes = encoded.toBytes(from: Encoding.base64);
- /// Creates an empty [ByteArray] object from a length.
- ByteArray.fromLength(int length) : _bytes = Uint8List(length);
+ /// Creates a [ByteArray] object from a hexdecimal string.
+ ByteArray.fromBase16(String encoded)
+ : _bytes = encoded.toBytes(from: Encoding.base16);
- /// Creates a [ByteArray] object from a [List] of [int].
- ByteArray.fromList(List list) : _bytes = list.toTypedList();
+ final Uint8List _bytes;
/// Gets the [ByteArray] bytes.
Uint8List get bytes => _bytes;
@@ -62,31 +63,6 @@ abstract class ByteArray {
/// Gets the [ByteArray] length in bytes.
int get length => _bytes.length;
- /// Gets the [ByteArray] length in bits.
- int get bitLength => _bytes.length * 8;
-
@override
- bool operator ==(Object other) {
- if (other is ByteArray) {
- for (int i = 0; i < _bytes.length; i++) {
- if (_bytes[i] != other._bytes[i]) {
- return false;
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- @override
- int get hashCode {
- int hash = 0;
- for (int i = 0; i < _bytes.length; i++) {
- hash = _bytes[i] + (hash << 6) + (hash << 16) - hash;
- }
-
- return hash;
- }
+ List get props => [_bytes];
}
diff --git a/packages/native_crypto/lib/src/domain/cipher.dart b/packages/native_crypto/lib/src/domain/cipher.dart
new file mode 100644
index 0000000..ee9a475
--- /dev/null
+++ b/packages/native_crypto/lib/src/domain/cipher.dart
@@ -0,0 +1,33 @@
+// Copyright 2019-2023 Hugo Pointcheval
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:native_crypto/src/core/utils/cipher_text.dart';
+import 'package:native_crypto/src/domain/cipher_chunk.dart';
+
+/// {@template cipher}
+/// Abstract class that defines the behavior of a Cipher.
+/// {@endtemplate}
+abstract class Cipher {
+ /// {@macro cipher}
+ const Cipher();
+
+ /// Encrypts a [Uint8List] and returns a [CipherText].
+ Future> encrypt(Uint8List plainText);
+
+ /// Decrypts a [CipherText] and returns a [Uint8List].
+ Future decrypt(CipherText cipherText);
+
+ /// Encrypts a File located at [plainTextFile] and saves the result
+ /// at [cipherTextFile].
+ Future encryptFile(File plainTextFile, File cipherTextFile);
+
+ /// Decrypts a File located at [cipherTextFile] and saves the result
+ /// at [plainTextFile].
+ Future