diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..74ade5c --- /dev/null +++ b/.drone.yml @@ -0,0 +1,17 @@ +kind: pipeline +type: docker +name: default + +steps: +- name: test:all + image: cirrusci/flutter:stable + commands: + - PATH=$PATH:$HOME/.pub-cache/bin + - flutter doctor + - flutter pub global activate melos + - melos bs + - melos run test:all + +trigger: + branch: + - master \ No newline at end of file diff --git a/native_crypto/.gitignore b/.gitignore similarity index 74% rename from native_crypto/.gitignore rename to .gitignore index 9a04984..4c39101 100644 --- a/native_crypto/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig -# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows -# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,java,jetbrains+all,kotlin,linux,objective-c,rust,swift,windows,xcode +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,dart,flutter,java,jetbrains+all,kotlin,linux,objective-c,rust,swift,windows,xcode ### Dart ### # See https://www.dartlang.org/guides/libraries/private-files @@ -95,7 +95,33 @@ lib/generated_plugin_registrant.dart !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -### Intellij+all ### +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### JetBrains+all ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 @@ -159,6 +185,9 @@ atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml +# SonarLint plugin +.idea/sonarlint/ + # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties @@ -171,46 +200,27 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser -### Intellij+all Patch ### -# Ignores the whole .idea folder and all .iml files -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 +### JetBrains+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. -*.iml -modules.xml -.idea/misc.xml -*.ipr +.idea/* -# Sonarlint plugin -.idea/sonarlint +!.idea/codeStyles +!.idea/runConfigurations ### Kotlin ### # Compiled class file -*.class # Log file -*.log # BlueJ files -*.ctxt # Mobile Tools for Java (J2ME) -.mtj.tmp/ # Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* ### Linux ### *~ @@ -234,7 +244,8 @@ hs_err_pid* .LSOverride # Icon must end with two \r -Icon +Icon + # Thumbnails ._* @@ -255,7 +266,11 @@ Network Trash Folder Temporary Items .apdisk -### Swift ### +### macOS Patch ### +# iCloud generated files +*.icloud + +### Objective-C ### # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore @@ -287,6 +302,64 @@ DerivedData/ *.dSYM.zip *.dSYM +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### Objective-C Patch ### + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### Swift ### +# Xcode +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + + + + + + ## Playgrounds timeline.xctimeline playground.xcworkspace @@ -315,7 +388,6 @@ playground.xcworkspace # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts -Carthage/Build/ # Accio dependency management Dependencies/ @@ -327,16 +399,11 @@ Dependencies/ # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots/**/*.png -fastlane/test_output # Code Injection # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode -iOSInjectionProject/ ### VisualStudioCode ### .vscode/* @@ -344,18 +411,24 @@ iOSInjectionProject/ !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json -*.code-workspace +!.vscode/*.code-snippets # Local History for Visual Studio Code .history/ +# Built Visual Studio Code Extensions +*.vsix + ### VisualStudioCode Patch ### # Ignore all local history of files .history .ionide # Support for Project snippet scope -!.vscode/*.code-snippets +.vscode/*.code-snippets + +# Ignore code-workspaces +*.code-workspace ### Windows ### # Windows thumbnail cache files @@ -383,7 +456,27 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows +### Xcode ### + +## Xcode 8 and earlier + +### Xcode Patch ### +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcworkspace/contents.xcworkspacedata +/*.gcno +**/xcshareddata/WorkspaceSettings.xcsettings + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,java,jetbrains+all,kotlin,linux,objective-c,rust,swift,windows,xcode # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ + +# Mac +.DS_Store \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d9becfd --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "native_crypto", + "cwd": "packages/native_crypto/example", + "request": "launch", + "type": "dart" + }, + { + "name": "native_crypto (profile mode)", + "cwd": "packages/native_crypto/example", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "native_crypto (release mode)", + "cwd": "packages/native_crypto/example", + "request": "launch", + "type": "dart", + "flutterMode": "release" + }, + ] +} \ No newline at end of file diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d9808a7 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to this project. Names should be added to the list like so: +# +# Name/Organization + +Hugo Pointcheval \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e4d77c8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 2022-05-25 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`native_crypto` - `v0.1.1`](#native_crypto---v011) + +--- + +#### `native_crypto` - `v0.1.1` + + - **REFACTOR**: change file organization. + - **PERF**: x10 perfomance improvement on android with better list management. + - **FIX**: benchmark output. + - **FIX**: update and fix code. + - **FEAT**: export new exceptions. + - **FEAT**: add PointyCastle benchmark. + - **DOCS**: add link to readme file. + + +## 2022-05-25 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`native_crypto_ios` - `v0.1.1`](#native_crypto_ios---v011) + - [`native_crypto_android` - `v0.1.1`](#native_crypto_android---v011) + - [`native_crypto_platform_interface` - `v0.1.1`](#native_crypto_platform_interface---v011) + +--- + +#### `native_crypto_ios` - `v0.1.1` + + - **REFACTOR**: rework swift part. + - **PERF**: optimize swift code. + +#### `native_crypto_android` - `v0.1.1` + + - **REFACTOR**: clean and modernize kotlin code. + - **PERF**: x10 perfomance improvement on android with better list management. + - **FEAT**: export new exceptions. + +#### `native_crypto_platform_interface` - `v0.1.1` + + - **PERF**: x10 perfomance improvement on android with better list management. + - **FEAT**: export new exceptions. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2f00d5b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..507b7ec --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +

+ +

Fast and powerful cryptographic functions for Flutter.
+

+ +

+ +Style: Wyatt Analysis + + + +Maintained with Melos + + + +Build Status + +

+ +--- + +[[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: + +![](resources/benchmarks.png) + +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 diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 0000000..a311fc5 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,57 @@ +name: NativeCrypto +# repository: https://git.pointcheval.fr/NativeCrypto/native-crypto-flutter + +packages: + - packages/** + +command: + version: + updateGitTagRefs: true + linkToCommits: false # Gitea not support this + workspaceChangelog: true + # branch: master + +scripts: + lint:all: + run: melos run analyze && melos run format + description: Run all static analysis checks. + + test:all: + run: | + melos run test --no-select + description: | + Run all tests available. + + test: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter test --no-pub --no-test-assets" + description: Run `flutter test` for a specific package. + select-package: + dir-exists: + - test + ignore: + - "*web*" + - "*example*" + + analyze: + run: | + melos exec -c 10 -- \ + flutter analyze --fatal-infos + description: Run `flutter analyze` for all packages. + + format: + run: melos exec flutter format . --fix + description: Run `flutter format` for all packages. + + format-check: + run: melos exec flutter format . --set-exit-if-changed + description: Run `flutter format` checks for all packages. + + clean:deep: + run: git clean -x -d -f -q + description: Clean things very deeply with `git clean`. + + # Additional cleanup lifecycle script, executed when `melos clean` is run. + postclean: > + melos exec -c 6 -- "flutter clean" \ No newline at end of file diff --git a/native_crypto/CHANGELOG.md b/native_crypto/CHANGELOG.md deleted file mode 100644 index 41cc7d8..0000000 --- a/native_crypto/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.0.1 - -* TODO: Describe initial release. diff --git a/native_crypto/LICENSE b/native_crypto/LICENSE deleted file mode 100644 index ba75c69..0000000 --- a/native_crypto/LICENSE +++ /dev/null @@ -1 +0,0 @@ -TODO: Add your license here. diff --git a/native_crypto/README.md b/native_crypto/README.md deleted file mode 100644 index 1bf39c0..0000000 --- a/native_crypto/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# native_crypto - -A new flutter plugin project. - -## Getting Started - -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. - -The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. -To add platforms, run `flutter create -t plugin --platforms .` under the same -directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. diff --git a/native_crypto/analysis_options.yaml b/native_crypto/analysis_options.yaml deleted file mode 100644 index a5744c1..0000000 --- a/native_crypto/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj b/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 6571d99..0000000 --- a/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,484 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 6Z5P8GG96U; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 6Z5P8GG96U; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 6Z5P8GG96U; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/native_crypto/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/native_crypto/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/native_crypto/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/native_crypto/example/lib/main.dart b/native_crypto/example/lib/main.dart deleted file mode 100644 index 322f70f..0000000 --- a/native_crypto/example/lib/main.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:native_crypto/native_crypto.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - String _platformVersion = 'Unknown'; - - @override - void initState() { - super.initState(); - initPlatformState(); - } - - // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - String platformVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - // We also handle the message potentially returning null. - try { - platformVersion = - await NativeCrypto.platformVersion ?? 'Unknown platform version'; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; - - setState(() { - _platformVersion = platformVersion; - }); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Text('Running on: $_platformVersion\n'), - ), - ), - ); - } -} diff --git a/native_crypto/example/test/widget_test.dart b/native_crypto/example/test/widget_test.dart deleted file mode 100644 index ffdf51c..0000000 --- a/native_crypto/example/test/widget_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:native_crypto_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} diff --git a/native_crypto/lib/native_crypto.dart b/native_crypto/lib/native_crypto.dart deleted file mode 100644 index 1036e3a..0000000 --- a/native_crypto/lib/native_crypto.dart +++ /dev/null @@ -1,17 +0,0 @@ -// You have generated a new plugin project without -// specifying the `--platforms` flag. A plugin project supports no platforms is generated. -// To add platforms, run `flutter create -t plugin --platforms .` under the same -// directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. - -import 'dart:async'; - -import 'package:flutter/services.dart'; - -class NativeCrypto { - static const MethodChannel _channel = MethodChannel('native_crypto'); - - static Future get platformVersion async { - final String? version = await _channel.invokeMethod('getPlatformVersion'); - return version; - } -} diff --git a/native_crypto/pubspec.yaml b/native_crypto/pubspec.yaml deleted file mode 100644 index 3baf2be..0000000 --- a/native_crypto/pubspec.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: native_crypto -description: Fast and secure cryptography for Flutter. -version: 0.0.7 -publish_to: 'none' - -environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" - -dependencies: - flutter: - sdk: flutter - - native_crypto_ios: - path: ../native_crypto_ios - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^1.0.4 - -flutter: - plugin: - platforms: - # android: - # default_package: native_crypto_android - ios: - default_package: native_crypto_ios \ No newline at end of file diff --git a/native_crypto/test/native_crypto_test.dart b/native_crypto/test/native_crypto_test.dart deleted file mode 100644 index 5caceab..0000000 --- a/native_crypto/test/native_crypto_test.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:native_crypto/native_crypto.dart'; - -void main() { - const MethodChannel channel = MethodChannel('native_crypto'); - - TestWidgetsFlutterBinding.ensureInitialized(); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); - }); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); - - test('getPlatformVersion', () async { - expect(await NativeCrypto.platformVersion, '42'); - }); -} diff --git a/native_crypto_ios/CHANGELOG.md b/native_crypto_ios/CHANGELOG.md deleted file mode 100644 index 41cc7d8..0000000 --- a/native_crypto_ios/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.0.1 - -* TODO: Describe initial release. diff --git a/native_crypto_ios/example/README.md b/native_crypto_ios/example/README.md deleted file mode 100644 index 340d8c1..0000000 --- a/native_crypto_ios/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# native_crypto_ios_example - -Demonstrates how to use the native_crypto_ios plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/native_crypto_ios/example/analysis_options.yaml b/native_crypto_ios/example/analysis_options.yaml deleted file mode 100644 index 61b6c4d..0000000 --- a/native_crypto_ios/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# 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 diff --git a/native_crypto_ios/example/ios/.gitignore b/native_crypto_ios/example/ios/.gitignore deleted file mode 100644 index 7a7f987..0000000 --- a/native_crypto_ios/example/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist b/native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 8d4492f..0000000 --- a/native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 9.0 - - diff --git a/native_crypto_ios/example/ios/Flutter/Debug.xcconfig b/native_crypto_ios/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6..0000000 --- a/native_crypto_ios/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/native_crypto_ios/example/ios/Flutter/Release.xcconfig b/native_crypto_ios/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bf..0000000 --- a/native_crypto_ios/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/native_crypto_ios/example/ios/Podfile b/native_crypto_ios/example/ios/Podfile deleted file mode 100644 index 1e8c3c9..0000000 --- a/native_crypto_ios/example/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index c87d15a..0000000 --- a/native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/native_crypto_ios/example/ios/Runner/AppDelegate.swift b/native_crypto_ios/example/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4..0000000 --- a/native_crypto_ios/example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fa..0000000 --- a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf0..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd9..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde121..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc230..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd9..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b86..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b86..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d16..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2..0000000 --- a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725..0000000 --- a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c..0000000 --- a/native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard b/native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c2851..0000000 --- a/native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/native_crypto_ios/example/ios/Runner/Info.plist b/native_crypto_ios/example/ios/Runner/Info.plist deleted file mode 100644 index 292a791..0000000 --- a/native_crypto_ios/example/ios/Runner/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Native Crypto Ios - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - native_crypto_ios_example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h b/native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a5..0000000 --- a/native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/native_crypto_ios/example/lib/main.dart b/native_crypto_ios/example/lib/main.dart deleted file mode 100644 index 6636074..0000000 --- a/native_crypto_ios/example/lib/main.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - String _platformVersion = 'Unknown'; - - @override - void initState() { - super.initState(); - initPlatformState(); - } - - // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - NativeCryptoPlatform _nativeCryptoPlatform = NativeCryptoPlatform.instance; - String platformVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - // We also handle the message potentially returning null. - Uint8List? sk = await _nativeCryptoPlatform.generateSecretKey(256); - print(sk ?? 'null'); - - Uint8List? ciphertext = await _nativeCryptoPlatform.encrypt(Uint8List.fromList("abc".codeUnits), sk!, "aes"); - print(ciphertext ?? 'null'); - - Uint8List? plaintext = await _nativeCryptoPlatform.decrypt(ciphertext!, sk, "aes"); - print(plaintext ?? 'null'); - - Uint8List? kp = await _nativeCryptoPlatform.generateKeyPair(); - print(kp!.sublist(0, 31)); - print(kp.sublist(32).length); - - Uint8List? sharedSecret = await _nativeCryptoPlatform.generateSharedSecretKey(Uint8List.fromList("salt".codeUnits), 32, kp.sublist(0, 31), kp.sublist(32), "sha256"); - - try { - platformVersion = 'Unknown platform version'; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; - - setState(() { - _platformVersion = platformVersion; - }); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Text('Running on: $_platformVersion\n'), - ), - ), - ); - } -} diff --git a/native_crypto_ios/example/pubspec.yaml b/native_crypto_ios/example/pubspec.yaml deleted file mode 100644 index ab0098f..0000000 --- a/native_crypto_ios/example/pubspec.yaml +++ /dev/null @@ -1,87 +0,0 @@ -name: native_crypto_ios_example -description: Demonstrates how to use the native_crypto_ios 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 - -environment: - sdk: ">=2.15.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 -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - native_crypto_ios: - # When depending on this package from a real application you should use: - # native_crypto_ios: ^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: ../ - - native_crypto_platform_interface: - path: ../../native_crypto_platform_interface - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - -dev_dependencies: - 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: ^1.0.0 - -# 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. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/native_crypto_ios/example/test/widget_test.dart b/native_crypto_ios/example/test/widget_test.dart deleted file mode 100644 index 75f369c..0000000 --- a/native_crypto_ios/example/test/widget_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:native_crypto_ios_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} diff --git a/native_crypto_ios/ios/Classes/Cipher.swift b/native_crypto_ios/ios/Classes/Cipher.swift deleted file mode 100644 index 7204680..0000000 --- a/native_crypto_ios/ios/Classes/Cipher.swift +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Author: Hugo Pointcheval - * Email: git@pcl.ovh - * ----- - * File: Cipher.swift - * Created Date: 25/12/2021 18:31:28 - * Last Modified: 25/12/2021 18:38:53 - * ----- - * Copyright (c) 2021 - */ - -import Foundation -import CryptoKit - -class AESCipher { - /// Encrypts plaintext with key using AES GCM - @available(iOS 13.0, *) - static func encrypt(plaintext: Data, key: Data) -> Data? { - let symmetricKey = SymmetricKey.init(data: key) - let encrypted = try? AES.GCM.seal(plaintext, using: symmetricKey) - return encrypted?.combined - } - - /// Decrypts ciphertext with key using AES GCM - @available(iOS 13.0, *) - static func decrypt(ciphertext: Data, key: Data) -> Data? { - let symmetricKey = SymmetricKey.init(data: key) - let sealedBox = try? AES.GCM.SealedBox(combined: ciphertext) - if (sealedBox == nil) { return nil } - let decryptedData = try? AES.GCM.open(sealedBox!, using: symmetricKey) - return decryptedData - } -} - -class CHACHACipher { - /// Encrypts plaintext with key using CHACHAPOLY - @available(iOS 13.0, *) - static func encrypt(plaintext: Data, key: Data) -> Data? { - let symmetricKey = SymmetricKey.init(data: key) - let encrypted = try? ChaChaPoly.seal(plaintext, using: symmetricKey) - return encrypted?.combined - } - - /// Decrypts ciphertext with key using CHACHAPOLY - @available(iOS 13.0, *) - static func decrypt(ciphertext: Data, key: Data) -> Data? { - let symmetricKey = SymmetricKey.init(data: key) - let sealedBox = try? ChaChaPoly.SealedBox(combined: ciphertext) - if (sealedBox == nil) { return nil } - let decryptedData = try? ChaChaPoly.open(sealedBox!, using: symmetricKey) - return decryptedData - } -} diff --git a/native_crypto_ios/ios/Classes/Hash.swift b/native_crypto_ios/ios/Classes/Hash.swift deleted file mode 100644 index 94990ec..0000000 --- a/native_crypto_ios/ios/Classes/Hash.swift +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Author: Hugo Pointcheval - * Email: git@pcl.ovh - * ----- - * File: Hash.swift - * Created Date: 25/12/2021 18:31:11 - * Last Modified: 25/12/2021 18:38:20 - * ----- - * Copyright (c) 2021 - */ - -import Foundation -import CommonCrypto -import CryptoKit - -enum HashAlgorithm: String { - case HashSHA256 = "sha256" - case HashSHA384 = "sha384" - case HashSHA512 = "sha512" - - var commonCrypto: UInt32 { - switch self { - case .HashSHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) - case .HashSHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) - case .HashSHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) - } - } -} - -@available(iOS 13.0, *) -class Hash { - /// Hash a message with a specified HashAlgorithm - static func digest(data: Data, algorithm: HashAlgorithm) -> Data { - switch algorithm { - case .HashSHA256: - return Data(SHA256.hash(data: data)) - case .HashSHA384: - return Data(SHA384.hash(data: data)) - case .HashSHA512: - return Data(SHA512.hash(data: data)) - } - } -} diff --git a/native_crypto_ios/ios/Classes/KEM.swift b/native_crypto_ios/ios/Classes/KEM.swift deleted file mode 100644 index cf3a27b..0000000 --- a/native_crypto_ios/ios/Classes/KEM.swift +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Author: Hugo Pointcheval - * Email: git@pcl.ovh - * ----- - * File: KEM.swift - * Created Date: 25/12/2021 18:31:48 - * Last Modified: 25/12/2021 18:40:00 - * ----- - * Copyright (c) 2021 - */ - -import Foundation -import CryptoKit - -class KeyPair { - /// Generate a keypair. - @available(iOS 13.0, *) - static func fromCurve() -> Data { - let sk = P256.KeyAgreement.PrivateKey() - var kp = sk.rawRepresentation - kp.append(contentsOf: sk.publicKey.rawRepresentation) - return kp; - } - - /// Import private key from Data - @available(iOS 13.0, *) - static func importPrivateKey(privateKey: Data) throws -> P256.KeyAgreement.PrivateKey { - let sk = try P256.KeyAgreement.PrivateKey(rawRepresentation: privateKey) - - return sk; - } - - /// Import public key from Data - @available(iOS 13.0, *) - static func importPublicKey(publicKey: Data) throws -> P256.KeyAgreement.PublicKey { - let pk = try P256.KeyAgreement.PublicKey(rawRepresentation: publicKey) - - return pk; - } -} - -class ECDH { - /// Generate a shared secret with your private key and other party public key. - @available(iOS 13.0, *) - static func generateSharedSecretKey(salt: Data, hash: HashAlgorithm, keyBytesCount: Int ,privateKey: Data, publicKey: Data) -> Data? { - let sk = try? KeyPair.importPrivateKey(privateKey: privateKey) - if (sk == nil) {return nil} - - let pk = try? KeyPair.importPublicKey(publicKey: publicKey) - if (pk == nil) {return nil} - - let secret = try? sk!.sharedSecretFromKeyAgreement(with: pk!) - - switch hash { - case .HashSHA256: - let key = secret?.hkdfDerivedSymmetricKey(using: SHA256.self, salt: salt, sharedInfo: Data(), outputByteCount: keyBytesCount) - if (key == nil) { - return nil - } else { - return Key.toBytes(key: key!) - } - case .HashSHA384: - let key = secret?.hkdfDerivedSymmetricKey(using: SHA384.self, salt: salt, sharedInfo: Data(), outputByteCount: keyBytesCount) - if (key == nil) { - return nil - } else { - return Key.toBytes(key: key!) - } - case .HashSHA512: - let key = secret?.hkdfDerivedSymmetricKey(using: SHA512.self, salt: salt, sharedInfo: Data(), outputByteCount: keyBytesCount) - if (key == nil) { - return nil - } else { - return Key.toBytes(key: key!) - } - } - } -} diff --git a/native_crypto_ios/ios/Classes/Key.swift b/native_crypto_ios/ios/Classes/Key.swift deleted file mode 100644 index b113df5..0000000 --- a/native_crypto_ios/ios/Classes/Key.swift +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Author: Hugo Pointcheval - * Email: git@pcl.ovh - * ----- - * File: KDF.swift - * Created Date: 25/12/2021 17:45:28 - * Last Modified: 25/12/2021 17:45:38 - * ----- - * Copyright (c) 2021 - */ - -import Foundation -import CryptoKit -import CommonCrypto - -class Key { - /// Generate secret key of a specified length - @available(iOS 13.0, *) - static func fromSecureRandom(bitsCount : Int) -> Data { - let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: bitsCount)) - return toBytes(key: symmetricKey) - } - - /// Encode key as Data - @available(iOS 13.0, *) - static func toBytes(key: SymmetricKey) -> Data { - let keyBytes = key.withUnsafeBytes - { - return Data(Array($0)) - } - return keyBytes - } - - /// Derive a new secret key with PBKDF2 algorithm - static func fromPBKDF2(password: String, salt: String, keyBytesCount: Int, iterations: Int, algorithm: HashAlgorithm) -> Data? { - let passwordData = password.data(using: .utf8)! - let saltData = salt.data(using: .utf8)! - - var derivedKeyData = Data(repeating: 0, count: keyBytesCount) - let localDerivedKeyData = derivedKeyData - - let status = derivedKeyData.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutableRawBufferPointer) in - saltData.withUnsafeBytes { (saltBytes: UnsafeRawBufferPointer) in - CCKeyDerivationPBKDF( - CCPBKDFAlgorithm(kCCPBKDF2), - password, - passwordData.count, - saltBytes.bindMemory(to: UInt8.self).baseAddress, - saltData.count, - algorithm.commonCrypto, - UInt32(iterations), - derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress, - localDerivedKeyData.count) - } - } - if (status != kCCSuccess) { - return nil; - } - - return derivedKeyData - } -} diff --git a/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift b/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift deleted file mode 100644 index 00fdbb7..0000000 --- a/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift +++ /dev/null @@ -1,95 +0,0 @@ -import Flutter -import UIKit - -@available(iOS 13.0, *) -public class SwiftNativeCryptoIosPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "plugins.hugop.cl/native_crypto", binaryMessenger: registrar.messenger()) - let instance = SwiftNativeCryptoIosPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "digest": - let args : NSDictionary = call.arguments as! NSDictionary - - let data : Data = (args["data"] as! FlutterStandardTypedData).data - let algo : String = args["algorithm"] as! String - let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) - // TODO(hpcl): check if algorithm is null - // TODO(hpcl): check if digest is null - result(FlutterStandardTypedData.init(bytes: Hash.digest(data: data, algorithm: algorithm!))) - case "generateSecretKey": - let args : NSDictionary = call.arguments as! NSDictionary - - let bitsCount : NSNumber = args["bitsCount"] as! NSNumber - // TODO(hpcl): check if secure random is null - result(FlutterStandardTypedData.init(bytes: Key.fromSecureRandom(bitsCount: bitsCount.intValue))) - case "generateKeyPair": - result(FlutterStandardTypedData.init(bytes: KeyPair.fromCurve())) - case "pbkdf2": - let args : NSDictionary = call.arguments as! NSDictionary - - let password : String = args["password"] as! String - let salt : String = args["salt"] as! String - let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber - let iterations : NSNumber = args["iterations"] as! NSNumber - let algo : String = args["algorithm"] as! String - let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) - // TODO(hpcl): check if algorithm is null - // TODO(hpcl): check if derivation is null - result(FlutterStandardTypedData.init(bytes: Key.fromPBKDF2(password: password, salt: salt, keyBytesCount: keyBytesCount.intValue, iterations: iterations.intValue, algorithm: algorithm!)!)) - case "encrypt": - let args : NSDictionary = call.arguments as! NSDictionary - - let data : Data = (args["data"] as! FlutterStandardTypedData).data - let key : Data = (args["key"] as! FlutterStandardTypedData).data - let algo : String = args["algorithm"] as! String - // TODO(hpcl): check if algorithm is null - // TODO(hpcl): check if ciphertext is null - var ciphertext : Data - switch algo { - case "aes": - ciphertext = AESCipher.encrypt(plaintext: data, key: key)! - case "chachapoly": - ciphertext = CHACHACipher.encrypt(plaintext: data, key: key)! - default: - ciphertext = Data.init(); - } - result(FlutterStandardTypedData.init(bytes: ciphertext)) - case "decrypt": - let args : NSDictionary = call.arguments as! NSDictionary - - let data : Data = (args["data"] as! FlutterStandardTypedData).data - let key : Data = (args["key"] as! FlutterStandardTypedData).data - let algo : String = args["algorithm"] as! String - // TODO(hpcl): check if algorithm is null - // TODO(hpcl): check if ciphertext is null - var ciphertext : Data - switch algo { - case "aes": - ciphertext = AESCipher.decrypt(ciphertext: data, key: key)! - case "chachapoly": - ciphertext = CHACHACipher.decrypt(ciphertext: data, key: key)! - default: - ciphertext = Data.init(); - } - result(FlutterStandardTypedData.init(bytes: ciphertext)) - case "generateSharedSecretKey": - let args : NSDictionary = call.arguments as! NSDictionary - - let salt : Data = (args["salt"] as! FlutterStandardTypedData).data - let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber - let ephemeralPrivateKey : Data = (args["ephemeralPrivateKey"] as! FlutterStandardTypedData).data - let otherPublicKey : Data = (args["otherPublicKey"] as! FlutterStandardTypedData).data - let hkdfAlgorithm : String = args["hkdfAlgorithm"] as! String - let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: hkdfAlgorithm) - // TODO(hpcl): check if algorithm is null - // TODO(hpcl): check if generated key is null - result(FlutterStandardTypedData.init(bytes: ECDH.generateSharedSecretKey(salt: salt, hash: algorithm!, keyBytesCount: keyBytesCount.intValue, privateKey: ephemeralPrivateKey, publicKey: otherPublicKey)!)) - - default: result(FlutterMethodNotImplemented) - } - } -} diff --git a/native_crypto_platform_interface/CHANGELOG.md b/native_crypto_platform_interface/CHANGELOG.md deleted file mode 100644 index 41cc7d8..0000000 --- a/native_crypto_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.0.1 - -* TODO: Describe initial release. diff --git a/native_crypto_platform_interface/LICENSE b/native_crypto_platform_interface/LICENSE deleted file mode 100644 index ba75c69..0000000 --- a/native_crypto_platform_interface/LICENSE +++ /dev/null @@ -1 +0,0 @@ -TODO: Add your license here. diff --git a/native_crypto_platform_interface/README.md b/native_crypto_platform_interface/README.md deleted file mode 100644 index 7190aa2..0000000 --- a/native_crypto_platform_interface/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# native_crypto_platform_interface - -A new flutter plugin project. - -## Getting Started - -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. - -The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. -To add platforms, run `flutter create -t plugin --platforms .` under the same -directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. diff --git a/native_crypto_platform_interface/analysis_options.yaml b/native_crypto_platform_interface/analysis_options.yaml deleted file mode 100644 index a5744c1..0000000 --- a/native_crypto_platform_interface/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart b/native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart deleted file mode 100644 index 3cab922..0000000 --- a/native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart +++ /dev/null @@ -1,107 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: native_crypto_method_channel.dart -// Created Date: 25/12/2021 16:58:04 -// Last Modified: 25/12/2021 18:58:53 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import '../native_crypto_platform_interface.dart'; - -/// An implementation of [NativeCryptoPlatform] that uses method channels. -class MethodChannelNativeCrypto extends NativeCryptoPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel methodChannel = - const MethodChannel('plugins.hugop.cl/native_crypto'); - - @override - Future digest(Uint8List data, String algorithm) { - return methodChannel.invokeMethod( - 'digest', - { - 'data': data, - 'algorithm': algorithm, - }, - ); - } - - @override - Future generateSecretKey(int bitsCount) { - return methodChannel.invokeMethod( - 'generateSecretKey', - { - 'bitsCount': bitsCount, - }, - ); - } - - @override - Future generateKeyPair() { - return methodChannel.invokeMethod('generateKeyPair'); - } - - @override - Future pbkdf2(String password, String salt, int keyBytesCount, - int iterations, String algorithm) { - return methodChannel.invokeMethod( - 'pbkdf2', - { - 'password': password, - 'salt': salt, - 'keyBytesCount': keyBytesCount, - 'iterations': iterations, - 'algorithm': algorithm, - }, - ); - } - - @override - Future encrypt(Uint8List data, Uint8List key, String algorithm) { - return methodChannel.invokeMethod( - 'encrypt', - { - 'data': data, - 'key': key, - 'algorithm': algorithm, - }, - ); - } - - @override - Future decrypt(Uint8List data, Uint8List key, String algorithm) { - return methodChannel.invokeMethod( - 'decrypt', - { - 'data': data, - 'key': key, - 'algorithm': algorithm, - }, - ); - } - - @override - Future generateSharedSecretKey( - Uint8List salt, - int keyBytesCount, - Uint8List ephemeralPrivateKey, - Uint8List otherPublicKey, - String hkdfAlgorithm) { - return methodChannel.invokeMethod( - 'generateSharedSecretKey', - { - 'salt': salt, - 'keyBytesCount': keyBytesCount, - 'ephemeralPrivateKey': ephemeralPrivateKey, - 'otherPublicKey': otherPublicKey, - 'hkdfAlgorithm': hkdfAlgorithm, - }, - ); - } -} diff --git a/native_crypto_platform_interface/lib/src/platform_interface.dart b/native_crypto_platform_interface/lib/src/platform_interface.dart deleted file mode 100644 index d0585c9..0000000 --- a/native_crypto_platform_interface/lib/src/platform_interface.dart +++ /dev/null @@ -1,97 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: platform_interface.dart -// Created Date: 25/12/2021 16:52:56 -// Last Modified: 25/12/2021 16:53:36 -// ----- -// Copyright (c) 2021 - -import 'package:meta/meta.dart'; - -/// Base class for platform interfaces. -/// -/// Provides a static helper method for ensuring that platform interfaces are -/// implemented using `extends` instead of `implements`. -/// -/// Platform interface classes are expected to have a private static token object which will be -/// be passed to [verifyToken] along with a platform interface object for verification. -/// -/// Sample usage: -/// -/// ```dart -/// abstract class UrlLauncherPlatform extends PlatformInterface { -/// UrlLauncherPlatform() : super(token: _token); -/// -/// static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); -/// -/// static const Object _token = Object(); -/// -/// static UrlLauncherPlatform get instance => _instance; -/// -/// /// Platform-specific plugins should set this with their own platform-specific -/// /// class that extends [UrlLauncherPlatform] when they register themselves. -/// static set instance(UrlLauncherPlatform instance) { -/// PlatformInterface.verifyToken(instance, _token); -/// _instance = instance; -/// } -/// -/// } -/// ``` -/// -/// Mockito mocks of platform interfaces will fail the verification, in test code only it is possible -/// to include the [MockPlatformInterfaceMixin] for the verification to be temporarily disabled. See -/// [MockPlatformInterfaceMixin] for a sample of using Mockito to mock a platform interface. -abstract class PlatformInterface { - /// Pass a private, class-specific `const Object()` as the `token`. - PlatformInterface({required Object token}) : _instanceToken = token; - - final Object? _instanceToken; - - /// Ensures that the platform instance has a token that matches the - /// provided token and throws [AssertionError] if not. - /// - /// This is used to ensure that implementers are using `extends` rather than - /// `implements`. - /// - /// Subclasses of [MockPlatformInterfaceMixin] are assumed to be valid in debug - /// builds. - /// - /// This is implemented as a static method so that it cannot be overridden - /// with `noSuchMethod`. - static void verifyToken(PlatformInterface instance, Object token) { - if (instance is MockPlatformInterfaceMixin) { - bool assertionsEnabled = false; - assert(() { - assertionsEnabled = true; - return true; - }()); - if (!assertionsEnabled) { - throw AssertionError( - '`MockPlatformInterfaceMixin` is not intended for use in release builds.'); - } - return; - } - if (!identical(token, instance._instanceToken)) { - throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); - } - } -} - -/// A [PlatformInterface] mixin that can be combined with mockito's `Mock`. -/// -/// It passes the [PlatformInterface.verifyToken] check even though it isn't -/// using `extends`. -/// -/// This class is intended for use in tests only. -/// -/// Sample usage (assuming UrlLauncherPlatform extends [PlatformInterface]: -/// -/// ```dart -/// class UrlLauncherPlatformMock extends Mock -/// with MockPlatformInterfaceMixin -/// implements UrlLauncherPlatform {} -/// ``` -@visibleForTesting -abstract class MockPlatformInterfaceMixin implements PlatformInterface {} \ No newline at end of file diff --git a/native_crypto_platform_interface/pubspec.yaml b/native_crypto_platform_interface/pubspec.yaml deleted file mode 100644 index 017ea35..0000000 --- a/native_crypto_platform_interface/pubspec.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: native_crypto_platform_interface -description: A common interface for NativeCrypto plugin. -version: 0.0.7 - -environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter - - flutter_lints: ^1.0.4 \ No newline at end of file diff --git a/native_crypto_platform_interface/test/native_crypto_platform_interface_test.dart b/native_crypto_platform_interface/test/native_crypto_platform_interface_test.dart deleted file mode 100644 index e69de29..0000000 diff --git a/native_crypto/.metadata b/packages/native_crypto/.metadata similarity index 100% rename from native_crypto/.metadata rename to packages/native_crypto/.metadata diff --git a/packages/native_crypto/CHANGELOG.md b/packages/native_crypto/CHANGELOG.md new file mode 100644 index 0000000..55a24cb --- /dev/null +++ b/packages/native_crypto/CHANGELOG.md @@ -0,0 +1,45 @@ +## 0.1.1 + + - **REFACTOR**: change file organization. + - **PERF**: x10 perfomance improvement on android with better list management. + - **FIX**: benchmark output. + - **FIX**: update and fix code. + - **FEAT**: export new exceptions. + - **FEAT**: add PointyCastle benchmark. + - **DOCS**: add link to readme file. + +## 0.1.0 + +> Breaking changes ! + +* Follow **Federated Plugin** Flutter standard. + +## 0.0.6 + +* Add KeyPair generation. +* Rework exposed API. + +## 0.0.5 + +* New API. +* Add digest support. +* Clean platform specific code base. + +## 0.0.4 + +* Improve AES. + +## 0.0.3 + +* Add PBKDF2 support. +* Add exceptions. +* Improve documentation. + +## 0.0.2 + +* Add different key size support. +* Improve performances. + +## 0.0.1 + +* First AES cross-platform encryption & decryption implementation. diff --git a/packages/native_crypto/LICENSE b/packages/native_crypto/LICENSE new file mode 100644 index 0000000..082d930 --- /dev/null +++ b/packages/native_crypto/LICENSE @@ -0,0 +1,23 @@ +NativeCrypto + +MIT License + +Copyright (c) 2019 - 2022 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +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 diff --git a/packages/native_crypto/README.md b/packages/native_crypto/README.md new file mode 100644 index 0000000..507b7ec --- /dev/null +++ b/packages/native_crypto/README.md @@ -0,0 +1,133 @@ +

+ +

Fast and powerful cryptographic functions for Flutter.
+

+ +

+ +Style: Wyatt Analysis + + + +Maintained with Melos + + + +Build Status + +

+ +--- + +[[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: + +![](resources/benchmarks.png) + +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 diff --git a/packages/native_crypto/analysis_options.yaml b/packages/native_crypto/analysis_options.yaml new file mode 100644 index 0000000..db48808 --- /dev/null +++ b/packages/native_crypto/analysis_options.yaml @@ -0,0 +1 @@ +include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml \ No newline at end of file diff --git a/native_crypto/example/.gitignore b/packages/native_crypto/example/.gitignore similarity index 100% rename from native_crypto/example/.gitignore rename to packages/native_crypto/example/.gitignore diff --git a/native_crypto/example/.metadata b/packages/native_crypto/example/.metadata similarity index 100% rename from native_crypto/example/.metadata rename to packages/native_crypto/example/.metadata diff --git a/native_crypto/example/README.md b/packages/native_crypto/example/README.md similarity index 100% rename from native_crypto/example/README.md rename to packages/native_crypto/example/README.md diff --git a/native_crypto/example/analysis_options.yaml b/packages/native_crypto/example/analysis_options.yaml similarity index 100% rename from native_crypto/example/analysis_options.yaml rename to packages/native_crypto/example/analysis_options.yaml diff --git a/native_crypto/example/android/.gitignore b/packages/native_crypto/example/android/.gitignore similarity index 100% rename from native_crypto/example/android/.gitignore rename to packages/native_crypto/example/android/.gitignore diff --git a/native_crypto/example/android/app/build.gradle b/packages/native_crypto/example/android/app/build.gradle similarity index 97% rename from native_crypto/example/android/app/build.gradle rename to packages/native_crypto/example/android/app/build.gradle index 803881a..15632c2 100644 --- a/native_crypto/example/android/app/build.gradle +++ b/packages/native_crypto/example/android/app/build.gradle @@ -44,7 +44,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "fr.pointcheval.native_crypto_example" - minSdkVersion flutter.minSdkVersion + minSdkVersion 26 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/native_crypto/example/android/app/src/debug/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from native_crypto/example/android/app/src/debug/AndroidManifest.xml rename to packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml diff --git a/native_crypto/example/android/app/src/main/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from native_crypto/example/android/app/src/main/AndroidManifest.xml rename to packages/native_crypto/example/android/app/src/main/AndroidManifest.xml diff --git a/native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt b/packages/native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt similarity index 100% rename from native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt rename to packages/native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt diff --git a/native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml rename to packages/native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/native_crypto/example/android/app/src/main/res/drawable/launch_background.xml b/packages/native_crypto/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from native_crypto/example/android/app/src/main/res/drawable/launch_background.xml rename to packages/native_crypto/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/native_crypto/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/native_crypto/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from native_crypto/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/native_crypto/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/native_crypto/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/native_crypto/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from native_crypto/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/native_crypto/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/native_crypto/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/native_crypto/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from native_crypto/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/native_crypto/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/native_crypto/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/native_crypto/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from native_crypto/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/native_crypto/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/native_crypto/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/native_crypto/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from native_crypto/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/native_crypto/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/native_crypto/example/android/app/src/main/res/values-night/styles.xml b/packages/native_crypto/example/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from native_crypto/example/android/app/src/main/res/values-night/styles.xml rename to packages/native_crypto/example/android/app/src/main/res/values-night/styles.xml diff --git a/native_crypto/example/android/app/src/main/res/values/styles.xml b/packages/native_crypto/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from native_crypto/example/android/app/src/main/res/values/styles.xml rename to packages/native_crypto/example/android/app/src/main/res/values/styles.xml diff --git a/native_crypto/example/android/app/src/profile/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from native_crypto/example/android/app/src/profile/AndroidManifest.xml rename to packages/native_crypto/example/android/app/src/profile/AndroidManifest.xml diff --git a/native_crypto/example/android/build.gradle b/packages/native_crypto/example/android/build.gradle similarity index 84% rename from native_crypto/example/android/build.gradle rename to packages/native_crypto/example/android/build.gradle index 24047dc..3245887 100644 --- a/native_crypto/example/android/build.gradle +++ b/packages/native_crypto/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = "1.6.21" repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/native_crypto/example/android/gradle.properties b/packages/native_crypto/example/android/gradle.properties similarity index 100% rename from native_crypto/example/android/gradle.properties rename to packages/native_crypto/example/android/gradle.properties diff --git a/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 93% rename from native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..562c5e4 100644 --- a/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-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip diff --git a/native_crypto/example/android/settings.gradle b/packages/native_crypto/example/android/settings.gradle similarity index 100% rename from native_crypto/example/android/settings.gradle rename to packages/native_crypto/example/android/settings.gradle diff --git a/native_crypto/example/ios/.gitignore b/packages/native_crypto/example/ios/.gitignore similarity index 100% rename from native_crypto/example/ios/.gitignore rename to packages/native_crypto/example/ios/.gitignore diff --git a/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist b/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from native_crypto/example/ios/Flutter/AppFrameworkInfo.plist rename to packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/native_crypto/example/ios/Flutter/Debug.xcconfig b/packages/native_crypto/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from native_crypto/example/ios/Flutter/Debug.xcconfig rename to packages/native_crypto/example/ios/Flutter/Debug.xcconfig diff --git a/native_crypto/example/ios/Flutter/Release.xcconfig b/packages/native_crypto/example/ios/Flutter/Release.xcconfig similarity index 100% rename from native_crypto/example/ios/Flutter/Release.xcconfig rename to packages/native_crypto/example/ios/Flutter/Release.xcconfig diff --git a/native_crypto/example/ios/Podfile b/packages/native_crypto/example/ios/Podfile similarity index 98% rename from native_crypto/example/ios/Podfile rename to packages/native_crypto/example/ios/Podfile index 1e8c3c9..10f3c9b 100644 --- a/native_crypto/example/ios/Podfile +++ b/packages/native_crypto/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/native_crypto_ios/example/ios/Podfile.lock b/packages/native_crypto/example/ios/Podfile.lock similarity index 73% rename from native_crypto_ios/example/ios/Podfile.lock rename to packages/native_crypto/example/ios/Podfile.lock index d6ba796..3257d42 100644 --- a/native_crypto_ios/example/ios/Podfile.lock +++ b/packages/native_crypto/example/ios/Podfile.lock @@ -15,8 +15,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - native_crypto_ios: 01f5aa926eb715d08259fd20bb951ba0f69c4e74 + native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d -PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c +PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.3 diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj similarity index 94% rename from native_crypto_ios/example/ios/Runner.xcodeproj/project.pbxproj rename to packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj index 7156cef..8c503eb 100644 --- a/native_crypto_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,8 +8,8 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 20AC864B3037BB896BD381B1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 056766980834B5FAC1C4D331 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 57C8B66CEF3FCADD27359CF2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B565CC9BF59F330E881E6A5 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -30,12 +30,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 056766980834B5FAC1C4D331 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 1180301722C337B6C625E46F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2A0F0FD6D80A80663D317892 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 2ABDB9EE0C984D50A4E8A819 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 4276EDF2B0F07350DCE3D5A1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 5CD4F461EBFD40A270B90468 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -46,7 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - EC40AF9C4AC2A38A0B8CC0E6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9B565CC9BF59F330E881E6A5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,19 +54,19 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 20AC864B3037BB896BD381B1 /* Pods_Runner.framework in Frameworks */, + 57C8B66CEF3FCADD27359CF2 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 3024D600AF0E0DED3BB44E03 /* Pods */ = { + 56AFD4323C9A594E66DE3CA2 /* Pods */ = { isa = PBXGroup; children = ( - EC40AF9C4AC2A38A0B8CC0E6 /* Pods-Runner.debug.xcconfig */, - 2A0F0FD6D80A80663D317892 /* Pods-Runner.release.xcconfig */, - 1180301722C337B6C625E46F /* Pods-Runner.profile.xcconfig */, + 4276EDF2B0F07350DCE3D5A1 /* Pods-Runner.debug.xcconfig */, + 2ABDB9EE0C984D50A4E8A819 /* Pods-Runner.release.xcconfig */, + 5CD4F461EBFD40A270B90468 /* Pods-Runner.profile.xcconfig */, ); name = Pods; path = Pods; @@ -89,8 +89,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 3024D600AF0E0DED3BB44E03 /* Pods */, - F589250F1A900F4BC6E4BB56 /* Frameworks */, + 56AFD4323C9A594E66DE3CA2 /* Pods */, + CECC25F8D34DF7912A93B0E6 /* Frameworks */, ); sourceTree = ""; }; @@ -117,10 +117,10 @@ path = Runner; sourceTree = ""; }; - F589250F1A900F4BC6E4BB56 /* Frameworks */ = { + CECC25F8D34DF7912A93B0E6 /* Frameworks */ = { isa = PBXGroup; children = ( - 056766980834B5FAC1C4D331 /* Pods_Runner.framework */, + 9B565CC9BF59F330E881E6A5 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -132,14 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - CE2C3E978245C5FF80E431AC /* [CP] Check Pods Manifest.lock */, + C54B0935BA386F7769969821 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ED05A5A0A8AF0FA8F71C2C2B /* [CP] Embed Pods Frameworks */, + D9E654BBB4893905628A201A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -226,7 +226,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - CE2C3E978245C5FF80E431AC /* [CP] Check Pods Manifest.lock */ = { + C54B0935BA386F7769969821 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -248,7 +248,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - ED05A5A0A8AF0FA8F71C2C2B /* [CP] Embed Pods Frameworks */ = { + D9E654BBB4893905628A201A /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -363,7 +363,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoIosExample; + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -492,7 +492,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoIosExample; + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -515,7 +515,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoIosExample; + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/native_crypto_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/native_crypto/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from native_crypto_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/native_crypto/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/native_crypto/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/native_crypto/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from native_crypto/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/native_crypto/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/native_crypto/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/native_crypto/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from native_crypto/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/native_crypto/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/native_crypto/example/ios/Runner/AppDelegate.swift b/packages/native_crypto/example/ios/Runner/AppDelegate.swift similarity index 100% rename from native_crypto/example/ios/Runner/AppDelegate.swift rename to packages/native_crypto/example/ios/Runner/AppDelegate.swift diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/native_crypto/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/native_crypto/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from native_crypto/example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/native_crypto/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/native_crypto/example/ios/Runner/Base.lproj/Main.storyboard b/packages/native_crypto/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from native_crypto/example/ios/Runner/Base.lproj/Main.storyboard rename to packages/native_crypto/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/native_crypto/example/ios/Runner/Info.plist b/packages/native_crypto/example/ios/Runner/Info.plist similarity index 96% rename from native_crypto/example/ios/Runner/Info.plist rename to packages/native_crypto/example/ios/Runner/Info.plist index 58d9657..8293c48 100644 --- a/native_crypto/example/ios/Runner/Info.plist +++ b/packages/native_crypto/example/ios/Runner/Info.plist @@ -43,5 +43,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/native_crypto/example/ios/Runner/Runner-Bridging-Header.h b/packages/native_crypto/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from native_crypto/example/ios/Runner/Runner-Bridging-Header.h rename to packages/native_crypto/example/ios/Runner/Runner-Bridging-Header.h diff --git a/packages/native_crypto/example/lib/home.dart b/packages/native_crypto/example/lib/home.dart new file mode 100644 index 0000000..8357b7e --- /dev/null +++ b/packages/native_crypto/example/lib/home.dart @@ -0,0 +1,64 @@ +// 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 new file mode 100644 index 0000000..65b60e0 --- /dev/null +++ b/packages/native_crypto/example/lib/main.dart @@ -0,0 +1,25 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: main.dart +// Created Date: 27/12/2021 21:15:12 +// Last Modified: 28/12/2021 13:51:36 +// ----- +// Copyright (c) 2021 + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:native_crypto_example/home.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()); + } +} diff --git a/packages/native_crypto/example/lib/pages/benchmark_page.dart b/packages/native_crypto/example/lib/pages/benchmark_page.dart new file mode 100644 index 0000000..6396152 --- /dev/null +++ b/packages/native_crypto/example/lib/pages/benchmark_page.dart @@ -0,0 +1,189 @@ +// 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 new file mode 100644 index 0000000..b0843c7 --- /dev/null +++ b/packages/native_crypto/example/lib/pages/cipher_page.dart @@ -0,0 +1,157 @@ +// 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 new file mode 100644 index 0000000..371d883 --- /dev/null +++ b/packages/native_crypto/example/lib/pages/kdf_page.dart @@ -0,0 +1,136 @@ +// 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 new file mode 100644 index 0000000..967c384 --- /dev/null +++ b/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart @@ -0,0 +1,80 @@ +// 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 + +// ignore_for_file: implementation_imports + +import 'dart:typed_data'; + +import 'package:pointycastle/export.dart'; +import 'package:pointycastle/src/platform_check/platform_check.dart'; + +class AesGcm { + FortunaRandom? _secureRandom; + + Uint8List encrypt(Uint8List data, Uint8List key) { + final iv = generateRandomBytes(12); + + final gcm = GCMBlockCipher(AESEngine()) + ..init( + true, + AEADParameters( + KeyParameter(key), + 16 * 8, + iv!, + Uint8List(0), + ), + ); // true=encrypt + + final cipherText = gcm.process(data); + + return Uint8List.fromList( + iv + cipherText, + ); + } + + Uint8List decrypt(Uint8List cipherText, Uint8List key) { + final iv = Uint8List.fromList(cipherText.sublist(0, 12)); + final cipherTextWithoutIv = Uint8List.fromList( + cipherText.sublist(12), + ); + + final gcm = GCMBlockCipher(AESEngine()) + ..init( + false, + AEADParameters( + KeyParameter(key), + 16 * 8, + iv, + Uint8List(0), + ), + ); // false=decrypt + + // Decrypt the cipherText block-by-block + + final paddedPlainText = gcm.process(cipherTextWithoutIv); + + return paddedPlainText; + } + + /// Generate random bytes to use as the Initialization Vector (IV). + Uint8List? generateRandomBytes(int numBytes) { + if (_secureRandom == null) { + // First invocation: create _secureRandom and seed it + + _secureRandom = FortunaRandom(); + _secureRandom!.seed( + KeyParameter(Platform.instance.platformEntropySource().getBytes(32))); + } + + // Use it to generate the random bytes + + final iv = _secureRandom!.nextBytes(numBytes); + return iv; + } +} diff --git a/packages/native_crypto/example/lib/session.dart b/packages/native_crypto/example/lib/session.dart new file mode 100644 index 0000000..7636866 --- /dev/null +++ b/packages/native_crypto/example/lib/session.dart @@ -0,0 +1,26 @@ +// 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 new file mode 100644 index 0000000..2244bb4 --- /dev/null +++ b/packages/native_crypto/example/lib/widgets/button.dart @@ -0,0 +1,31 @@ +// 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 new file mode 100644 index 0000000..1dc9b70 --- /dev/null +++ b/packages/native_crypto/example/lib/widgets/output.dart @@ -0,0 +1,61 @@ +// 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/native_crypto/example/pubspec.yaml b/packages/native_crypto/example/pubspec.yaml similarity index 97% rename from native_crypto/example/pubspec.yaml rename to packages/native_crypto/example/pubspec.yaml index e6e3ec3..7fb09c7 100644 --- a/native_crypto/example/pubspec.yaml +++ b/packages/native_crypto/example/pubspec.yaml @@ -29,6 +29,8 @@ dependencies: # 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 dev_dependencies: flutter_test: @@ -39,7 +41,7 @@ dev_dependencies: # 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: ^1.0.0 + flutter_lints: ^1.0.4 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/packages/native_crypto/lib/native_crypto.dart b/packages/native_crypto/lib/native_crypto.dart new file mode 100644 index 0000000..2b294a4 --- /dev/null +++ b/packages/native_crypto/lib/native_crypto.dart @@ -0,0 +1,30 @@ +// 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 + +/// Fast and powerful cryptographic functions +/// thanks to javax.crypto, CommonCrypto and CryptoKit. +/// +/// Author: Hugo Pointcheval +library native_crypto; + +export 'package:native_crypto_platform_interface/src/utils/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'; diff --git a/packages/native_crypto/lib/native_crypto_ext.dart b/packages/native_crypto/lib/native_crypto_ext.dart new file mode 100644 index 0000000..0e9502e --- /dev/null +++ b/packages/native_crypto/lib/native_crypto_ext.dart @@ -0,0 +1,11 @@ +// 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 new file mode 100644 index 0000000..d846197 --- /dev/null +++ b/packages/native_crypto/lib/src/builders/builders.dart @@ -0,0 +1,10 @@ +// 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 + +export 'decryption_builder.dart'; diff --git a/packages/native_crypto/lib/src/builders/decryption_builder.dart b/packages/native_crypto/lib/src/builders/decryption_builder.dart new file mode 100644 index 0000000..998fdcf --- /dev/null +++ b/packages/native_crypto/lib/src/builders/decryption_builder.dart @@ -0,0 +1,46 @@ +// 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 + +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'; + +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; + + const DecryptionBuilder({ + super.key, + required this.cipher, + required this.data, + required this.onLoading, + required this.onError, + required this.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); + }, + ); + } +} diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes.dart b/packages/native_crypto/lib/src/ciphers/aes/aes.dart new file mode 100644 index 0000000..b1113fc --- /dev/null +++ b/packages/native_crypto/lib/src/ciphers/aes/aes.dart @@ -0,0 +1,181 @@ +// 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 + +import 'dart:typed_data'; + +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/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_key_size.dart'; +export 'aes_mode.dart'; +export 'aes_padding.dart'; + +/// An AES cipher. +/// +/// [AES] is a [Cipher] that can be used to encrypt or decrypt data. +class AES implements Cipher { + final SecretKey _key; + final AESMode mode; + final AESPadding padding; + + @override + CipherAlgorithm get algorithm => CipherAlgorithm.aes; + + 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, + ); + } + + if (!mode.supportedPaddings.contains(padding)) { + throw NativeCryptoException( + message: 'Invalid padding! ' + 'Expected: ${mode.supportedPaddings.join(', ')}', + code: NativeCryptoExceptionCode.invalid_padding.code, + ); + } + } + + Future _decrypt( + CipherText cipherText, { + int chunkCount = 0, + }) async { + Uint8List? decrypted; + + try { + decrypted = await platform.decrypt( + cipherText.bytes, + _key.bytes, + algorithm.name, + ); + } catch (e, s) { + throw NativeCryptoException( + message: '$e', + code: NativeCryptoExceptionCode.platform_throws.code, + stackTrace: s, + ); + } + + 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; + } +} 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 new file mode 100644 index 0000000..befa22f --- /dev/null +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart @@ -0,0 +1,26 @@ +// 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 + +/// Defines all available key sizes. +enum AESKeySize { + bits128(128), + bits192(192), + bits256(256); + + /// Returns the number of bits supported by an [AESKeySize]. + static final List supportedSizes = [128, 192, 256]; + + /// Returns the number of bits in this [AESKeySize]. + final int bits; + + /// 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 new file mode 100644 index 0000000..4bbc7c4 --- /dev/null +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart @@ -0,0 +1,30 @@ +// 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 + +import 'package:native_crypto/src/ciphers/aes/aes_padding.dart'; + +/// Defines the AES modes of operation. +enum AESMode { + gcm([AESPadding.none], 12, 16); + + /// Returns the list of supported [AESPadding] for this [AESMode]. + final List supportedPaddings; + + /// Returns the default IV length for this [AESMode]. + final int ivLength; + + /// 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 new file mode 100644 index 0000000..343ae03 --- /dev/null +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart @@ -0,0 +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 + +/// Represents different paddings. +enum AESPadding { none } diff --git a/packages/native_crypto/lib/src/ciphers/ciphers.dart b/packages/native_crypto/lib/src/ciphers/ciphers.dart new file mode 100644 index 0000000..edae6a4 --- /dev/null +++ b/packages/native_crypto/lib/src/ciphers/ciphers.dart @@ -0,0 +1,10 @@ +// 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 + +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 new file mode 100644 index 0000000..6accd59 --- /dev/null +++ b/packages/native_crypto/lib/src/core/cipher_text.dart @@ -0,0 +1,117 @@ +// 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 new file mode 100644 index 0000000..75a9dae --- /dev/null +++ b/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart @@ -0,0 +1,191 @@ +// 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/core.dart b/packages/native_crypto/lib/src/core/core.dart new file mode 100644 index 0000000..32ad783 --- /dev/null +++ b/packages/native_crypto/lib/src/core/core.dart @@ -0,0 +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 + +export 'cipher_text.dart'; +export 'cipher_text_wrapper.dart'; diff --git a/packages/native_crypto/lib/src/interfaces/base_key.dart b/packages/native_crypto/lib/src/interfaces/base_key.dart new file mode 100644 index 0000000..e8ac27e --- /dev/null +++ b/packages/native_crypto/lib/src/interfaces/base_key.dart @@ -0,0 +1,22 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: base_key.dart +// Created Date: 16/12/2021 16:28:00 +// Last Modified: 26/05/2022 17:40:38 +// ----- +// Copyright (c) 2021 + +import 'package:native_crypto/src/interfaces/byte_array.dart'; + +/// 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. +abstract class BaseKey extends ByteArray { + const BaseKey(super.bytes); + BaseKey.fromBase16(super.encoded) : super.fromBase16(); + BaseKey.fromBase64(super.encoded) : super.fromBase64(); + BaseKey.fromUtf8(super.input) : super.fromUtf8(); +} diff --git a/packages/native_crypto/lib/src/interfaces/builder.dart b/packages/native_crypto/lib/src/interfaces/builder.dart new file mode 100644 index 0000000..a1b39aa --- /dev/null +++ b/packages/native_crypto/lib/src/interfaces/builder.dart @@ -0,0 +1,14 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: builder.dart +// Created Date: 28/12/2021 12:02:34 +// Last Modified: 23/05/2022 22:38:44 +// ----- +// Copyright (c) 2021 + +// ignore_for_file: one_member_abstracts + +abstract class Builder { + Future build(); +} diff --git a/packages/native_crypto/lib/src/interfaces/byte_array.dart b/packages/native_crypto/lib/src/interfaces/byte_array.dart new file mode 100644 index 0000000..7cdef3a --- /dev/null +++ b/packages/native_crypto/lib/src/interfaces/byte_array.dart @@ -0,0 +1,92 @@ +// 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 + +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'; + +/// 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]. + 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 Base64 string. + ByteArray.fromBase64(String encoded) + : _bytes = encoded.toBytes(from: Encoding.base64); + + /// 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 an empty [ByteArray] object from a length. + ByteArray.fromLength(int length) : _bytes = Uint8List(length); + + /// Creates a [ByteArray] object from a [List] of [int]. + ByteArray.fromList(List list) : _bytes = list.toTypedList(); + + /// Gets the [ByteArray] bytes. + Uint8List get bytes => _bytes; + + /// Gets the [ByteArray] bytes as a Hexadecimal representation. + String get base16 => _bytes.toStr(to: Encoding.base16); + + /// Gets the [ByteArray] bytes as a Base64 representation. + String get base64 => _bytes.toStr(to: Encoding.base64); + + /// Gets the [ByteArray] bytes as an UTF-8 representation. + String get utf8 => _bytes.toStr(to: Encoding.utf8); + + /// Gets the [ByteArray] bytes as an UTF-16 representation. + String get utf16 => _bytes.toStr(); + + /// 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; + } +} diff --git a/packages/native_crypto/lib/src/interfaces/cipher.dart b/packages/native_crypto/lib/src/interfaces/cipher.dart new file mode 100644 index 0000000..58b0d4a --- /dev/null +++ b/packages/native_crypto/lib/src/interfaces/cipher.dart @@ -0,0 +1,53 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: cipher.dart +// Created Date: 16/12/2021 16:28:00 +// Last Modified: 26/05/2022 21:21:07 +// ----- +// Copyright (c) 2021 + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; +import 'package:native_crypto/src/utils/cipher_algorithm.dart'; + +/// Represents a cipher in NativeCrypto. +/// +/// In cryptography, a [Cipher] is an algorithm for performing encryption +/// or decryption - a series of well-defined steps that can +/// be followed as a procedure. +/// +/// This interface is implemented by all the ciphers in NativeCrypto. +abstract class Cipher { + static const int _bytesCountPerChunkDefault = 33554432; + static int _bytesCountPerChunk = _bytesCountPerChunkDefault; + + /// Returns the default number of bytes per chunk. + static int get defaultBytesCountPerChunk => _bytesCountPerChunkDefault; + + /// Returns the size of a chunk of data + /// that can be processed by the [Cipher]. + static int get bytesCountPerChunk => Cipher._bytesCountPerChunk; + + /// Sets the size of a chunk of data + /// that can be processed by the [Cipher]. + static set bytesCountPerChunk(int bytesCount) { + _bytesCountPerChunk = bytesCount; + } + + /// Returns the standard algorithm for this [Cipher]. + CipherAlgorithm get algorithm; + + /// Encrypts the [data]. + /// + /// Takes [Uint8List] data as parameter. + /// Returns a [CipherTextWrapper]. + Future encrypt(Uint8List data); + + /// Decrypts the [cipherText] + /// + /// Takes [CipherTextWrapper] as parameter. + /// And returns plain text data as [Uint8List]. + Future decrypt(CipherTextWrapper cipherText); +} diff --git a/packages/native_crypto/lib/src/interfaces/interfaces.dart b/packages/native_crypto/lib/src/interfaces/interfaces.dart new file mode 100644 index 0000000..ef47be3 --- /dev/null +++ b/packages/native_crypto/lib/src/interfaces/interfaces.dart @@ -0,0 +1,14 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: interfaces.dart +// Created Date: 23/05/2022 23:03:47 +// Last Modified: 26/05/2022 17:41:06 +// ----- +// Copyright (c) 2022 + +export 'base_key.dart'; +export 'builder.dart'; +export 'byte_array.dart'; +export 'cipher.dart'; +export 'keyderivation.dart'; diff --git a/packages/native_crypto/lib/src/interfaces/keyderivation.dart b/packages/native_crypto/lib/src/interfaces/keyderivation.dart new file mode 100644 index 0000000..ccdbb4b --- /dev/null +++ b/packages/native_crypto/lib/src/interfaces/keyderivation.dart @@ -0,0 +1,23 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: kdf.dart +// Created Date: 18/12/2021 11:56:43 +// Last Modified: 26/05/2022 18:47:15 +// ----- +// Copyright (c) 2021 + +import 'package:native_crypto/src/keys/secret_key.dart'; +import 'package:native_crypto/src/utils/kdf_algorithm.dart'; + +/// Represents a Key Derivation Function (KDF) in NativeCrypto. +/// +/// [KeyDerivation] function is a function that takes some +/// parameters and returns a [SecretKey]. +abstract class KeyDerivation { + /// Returns the standard algorithm for this key derivation function + KdfAlgorithm get algorithm; + + /// Derive a [SecretKey]. + Future derive(); +} diff --git a/packages/native_crypto/lib/src/kdf/kdf.dart b/packages/native_crypto/lib/src/kdf/kdf.dart new file mode 100644 index 0000000..cb7d609 --- /dev/null +++ b/packages/native_crypto/lib/src/kdf/kdf.dart @@ -0,0 +1,10 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: kdf.dart +// Created Date: 23/05/2022 22:57:11 +// Last Modified: 23/05/2022 23:04:15 +// ----- +// Copyright (c) 2022 + +export 'pbkdf2.dart'; diff --git a/packages/native_crypto/lib/src/kdf/pbkdf2.dart b/packages/native_crypto/lib/src/kdf/pbkdf2.dart new file mode 100644 index 0000000..8ccdadd --- /dev/null +++ b/packages/native_crypto/lib/src/kdf/pbkdf2.dart @@ -0,0 +1,115 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: pbkdf2.dart +// Created Date: 17/12/2021 14:50:42 +// Last Modified: 26/05/2022 23:19:46 +// ----- +// Copyright (c) 2021 + +import 'dart:typed_data'; + +import 'package:native_crypto/src/interfaces/keyderivation.dart'; +import 'package:native_crypto/src/keys/secret_key.dart'; +import 'package:native_crypto/src/platform.dart'; +import 'package:native_crypto/src/utils/extensions.dart'; +import 'package:native_crypto/src/utils/hash_algorithm.dart'; +import 'package:native_crypto/src/utils/kdf_algorithm.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +/// Represent a PBKDF2 Key Derivation Function (KDF) in NativeCrypto. +/// +/// [Pbkdf2] is a function that takes password, salt, iteration count and +/// derive a [SecretKey] of specified length. +class Pbkdf2 extends KeyDerivation { + final int _keyBytesCount; + final int _iterations; + final HashAlgorithm _hash; + + @override + KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2; + + Pbkdf2({ + required int keyBytesCount, + required int iterations, + HashAlgorithm algorithm = HashAlgorithm.sha256, + }) : _keyBytesCount = keyBytesCount, + _iterations = iterations, + _hash = algorithm { + if (keyBytesCount < 0) { + throw NativeCryptoException( + message: 'keyBytesCount must be positive.', + code: NativeCryptoExceptionCode.invalid_argument.code, + ); + } + + if (iterations <= 0) { + throw NativeCryptoException( + message: 'iterations must be strictly positive.', + code: NativeCryptoExceptionCode.invalid_argument.code, + ); + } + } + + @override + Future derive({String? password, String? salt}) async { + Uint8List? derivation; + + if (_keyBytesCount == 0) { + return SecretKey(Uint8List(0)); + } + if (password.isNull) { + throw NativeCryptoException( + message: 'Password cannot be null.', + code: NativeCryptoExceptionCode.invalid_argument.code, + ); + } + + if (salt.isNull) { + throw NativeCryptoException( + message: 'Salt cannot be null.', + code: NativeCryptoExceptionCode.invalid_argument.code, + ); + } + + try { + derivation = await platform.pbkdf2( + password!, + salt!, + _keyBytesCount, + _iterations, + _hash.name, + ); + } catch (e, s) { + throw NativeCryptoException( + message: '$e', + code: NativeCryptoExceptionCode.platform_throws.code, + stackTrace: s, + ); + } + + if (derivation.isNull) { + throw NativeCryptoException( + message: 'Failed to derive a key! Platform returned null.', + code: NativeCryptoExceptionCode.platform_returned_null.code, + ); + } + + if (derivation!.isEmpty) { + throw NativeCryptoException( + message: 'Failed to derive a key! Platform returned no data.', + code: NativeCryptoExceptionCode.platform_returned_empty_data.code, + ); + } + + if (derivation.length != _keyBytesCount) { + throw NativeCryptoException( + message: 'Failed to derive a key! Platform returned ' + '${derivation.length} bytes, but expected $_keyBytesCount bytes.', + code: NativeCryptoExceptionCode.platform_returned_invalid_data.code, + ); + } + + return SecretKey(derivation); + } +} diff --git a/packages/native_crypto/lib/src/keys/keys.dart b/packages/native_crypto/lib/src/keys/keys.dart new file mode 100644 index 0000000..912bb39 --- /dev/null +++ b/packages/native_crypto/lib/src/keys/keys.dart @@ -0,0 +1,10 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: keys.dart +// Created Date: 23/05/2022 23:04:04 +// Last Modified: 23/05/2022 23:04:07 +// ----- +// Copyright (c) 2022 + +export 'secret_key.dart'; diff --git a/packages/native_crypto/lib/src/keys/secret_key.dart b/packages/native_crypto/lib/src/keys/secret_key.dart new file mode 100644 index 0000000..e30b87b --- /dev/null +++ b/packages/native_crypto/lib/src/keys/secret_key.dart @@ -0,0 +1,60 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: secret_key.dart +// Created Date: 28/12/2021 13:36:54 +// Last Modified: 26/05/2022 23:13:10 +// ----- +// Copyright (c) 2021 + +import 'dart:typed_data'; + +import 'package:native_crypto/src/interfaces/base_key.dart'; +import 'package:native_crypto/src/interfaces/cipher.dart'; +import 'package:native_crypto/src/platform.dart'; +import 'package:native_crypto/src/utils/extensions.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +/// Represents a secret key in NativeCrypto. +/// +/// [SecretKey] is a [BaseKey] that can be used to store secret keys. +/// A [SecretKey] is a key that can be used to encrypt or decrypt data with +/// a symmetric [Cipher]. +class SecretKey extends BaseKey { + const SecretKey(super.bytes); + SecretKey.fromBase16(super.encoded) : super.fromBase16(); + SecretKey.fromBase64(super.encoded) : super.fromBase64(); + SecretKey.fromUtf8(super.input) : super.fromUtf8(); + + static Future fromSecureRandom(int bitsCount) async { + Uint8List? key; + if (bitsCount == 0) { + return SecretKey(Uint8List(0)); + } + + try { + key = await platform.generateSecretKey(bitsCount); + } catch (e, s) { + throw NativeCryptoException( + message: '$e', + code: NativeCryptoExceptionCode.platform_throws.code, + stackTrace: s, + ); + } + if (key.isNull) { + throw NativeCryptoException( + message: 'Failed to generate a secret key! Platform returned null.', + code: NativeCryptoExceptionCode.platform_returned_null.code, + ); + } + + if (key!.isEmpty) { + throw NativeCryptoException( + message: 'Failed to generate a secret key! ' + 'Platform returned no data.', + code: NativeCryptoExceptionCode.platform_returned_empty_data.code, + ); + } + return SecretKey(key); + } +} diff --git a/packages/native_crypto/lib/src/platform.dart b/packages/native_crypto/lib/src/platform.dart new file mode 100644 index 0000000..5d62b5e --- /dev/null +++ b/packages/native_crypto/lib/src/platform.dart @@ -0,0 +1,12 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: platform.dart +// Created Date: 27/12/2021 22:03:58 +// Last Modified: 25/05/2022 10:09:18 +// ----- +// Copyright (c) 2021 + +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +NativeCryptoPlatform platform = NativeCryptoPlatform.instance; diff --git a/packages/native_crypto/lib/src/utils/cipher_algorithm.dart b/packages/native_crypto/lib/src/utils/cipher_algorithm.dart new file mode 100644 index 0000000..2ba968c --- /dev/null +++ b/packages/native_crypto/lib/src/utils/cipher_algorithm.dart @@ -0,0 +1,11 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: cipher_algorithm.dart +// Created Date: 23/05/2022 22:07:54 +// Last Modified: 26/05/2022 18:52:32 +// ----- +// Copyright (c) 2022 + +/// Represents different cipher algorithms +enum CipherAlgorithm { aes } diff --git a/packages/native_crypto/lib/src/utils/encoding.dart b/packages/native_crypto/lib/src/utils/encoding.dart new file mode 100644 index 0000000..b7ddd80 --- /dev/null +++ b/packages/native_crypto/lib/src/utils/encoding.dart @@ -0,0 +1,10 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: encoding.dart +// Created Date: 26/05/2022 12:12:34 +// Last Modified: 26/05/2022 12:18:09 +// ----- +// Copyright (c) 2022 + +enum Encoding { utf8, utf16, base64, base16 } diff --git a/packages/native_crypto/lib/src/utils/extensions.dart b/packages/native_crypto/lib/src/utils/extensions.dart new file mode 100644 index 0000000..fc2a799 --- /dev/null +++ b/packages/native_crypto/lib/src/utils/extensions.dart @@ -0,0 +1,101 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: extensions.dart +// Created Date: 26/05/2022 12:12:48 +// Last Modified: 27/05/2022 12:26:55 +// ----- +// Copyright (c) 2022 + +import 'dart:convert'; +import 'dart:developer' as developer; +import 'dart:typed_data'; + +import 'package:native_crypto/src/utils/encoding.dart'; + +extension ObjectX on Object? { + /// Returns `true` if the object is `null`. + bool get isNull => this == null; + + /// Returns `true` if the object is **not** `null`. + bool get isNotNull => this != null; + + /// Prints the object to the console. + void log() => developer.log(toString()); +} + +extension ListIntX on List { + /// Converts a [List] of int to a [Uint8List]. + Uint8List toTypedList() => Uint8List.fromList(this); +} + +extension ListUint8ListX on List { + /// Reduce a [List] of [Uint8List] to a [Uint8List]. + Uint8List combine() { + if (isEmpty) return Uint8List(0); + return reduce((value, element) => value.plus(element)); + } +} + +extension StringX on String { + /// Converts a [String] to a [Uint8List] using the specified [Encoding]. + Uint8List toBytes({final 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; + } +} + +extension Uint8ListX on Uint8List { + /// Converts a [Uint8List] to a [String] using the specified [Encoding]. + String toStr({final 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; + } + + /// Returns a concatenation of this with the other [Uint8List]. + Uint8List plus(final Uint8List other) => [...this, ...other].toTypedList(); + + /// 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); + } +} diff --git a/packages/native_crypto/lib/src/utils/hash_algorithm.dart b/packages/native_crypto/lib/src/utils/hash_algorithm.dart new file mode 100644 index 0000000..4538ef5 --- /dev/null +++ b/packages/native_crypto/lib/src/utils/hash_algorithm.dart @@ -0,0 +1,51 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: hash_algorithm.dart +// Created Date: 23/05/2022 22:01:59 +// Last Modified: 26/05/2022 22:59:04 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:native_crypto/src/platform.dart'; +import 'package:native_crypto/src/utils/extensions.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +/// Defines the hash algorithms. +enum HashAlgorithm { + sha256, + sha384, + sha512; + + /// Digest the [data] using this [HashAlgorithm]. + Future digest(Uint8List data) async { + Uint8List? hash; + try { + hash = await platform.digest(data, name); + } catch (e, s) { + throw NativeCryptoException( + message: '$e', + code: NativeCryptoExceptionCode.platform_throws.code, + stackTrace: s, + ); + } + + if (hash.isNull) { + throw NativeCryptoException( + message: 'Failed to digest data! Platform returned null.', + code: NativeCryptoExceptionCode.platform_returned_null.code, + ); + } + + if (hash!.isEmpty) { + throw NativeCryptoException( + message: 'Failed to digest data! Platform returned no data.', + code: NativeCryptoExceptionCode.platform_returned_empty_data.code, + ); + } + + return hash; + } +} diff --git a/packages/native_crypto/lib/src/utils/kdf_algorithm.dart b/packages/native_crypto/lib/src/utils/kdf_algorithm.dart new file mode 100644 index 0000000..68d6a76 --- /dev/null +++ b/packages/native_crypto/lib/src/utils/kdf_algorithm.dart @@ -0,0 +1,11 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: kdf_algorithm.dart +// Created Date: 23/05/2022 22:36:24 +// Last Modified: 26/05/2022 18:53:50 +// ----- +// Copyright (c) 2022 + +/// Represents different key derivation functions +enum KdfAlgorithm { pbkdf2 } diff --git a/packages/native_crypto/pubspec.yaml b/packages/native_crypto/pubspec.yaml new file mode 100644 index 0000000..72b0d59 --- /dev/null +++ b/packages/native_crypto/pubspec.yaml @@ -0,0 +1,52 @@ +name: native_crypto +description: Fast and secure cryptography for Flutter. +version: 0.1.1 + +publish_to: 'none' + +environment: + sdk: ">=2.17.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + + native_crypto_android: + git: + url: https://github.com/hugo-pcl/native-crypto-flutter.git + ref: native_crypto_android-v0.1.1 + path: packages/native_crypto_android + + native_crypto_ios: + git: + url: https://github.com/hugo-pcl/native-crypto-flutter.git + ref: native_crypto_ios-v0.1.1 + path: packages/native_crypto_ios + + native_crypto_platform_interface: + git: + url: https://github.com/hugo-pcl/native-crypto-flutter.git + ref: native_crypto_platform_interface-v0.1.1 + path: packages/native_crypto_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + plugin_platform_interface: ^2.1.2 + + wyatt_analysis: + git: + url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages + ref: wyatt_analysis-v2.1.0 + path: packages/wyatt_analysis + +flutter: + plugin: + platforms: + android: + default_package: native_crypto_android + ios: + default_package: native_crypto_ios \ No newline at end of file diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart b/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart new file mode 100644 index 0000000..6e69cb9 --- /dev/null +++ b/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart @@ -0,0 +1,192 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: mock_native_crypto_platform.dart +// Created Date: 25/05/2022 23:34:34 +// Last Modified: 26/05/2022 11:40:24 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockNativeCryptoPlatform extends Fake + with MockPlatformInterfaceMixin + implements NativeCryptoPlatform { + Uint8List? data; + List? dataAsList; + Uint8List? key; + String? algorithm; + int? bitsCount; + String? password; + String? salt; + int? keyBytesCount; + int? iterations; + + Uint8List? Function()? response; + List? Function()? responseAsList; + + // ignore: use_setters_to_change_properties + void setResponse(Uint8List? Function()? response) { + this.response = response; + } + + // ignore: use_setters_to_change_properties + void setResponseAsList(List? Function()? responseAsList) { + this.responseAsList = responseAsList; + } + + void setDecryptExpectations({ + required Uint8List data, + required Uint8List key, + required String algorithm, + }) { + this.data = data; + this.key = key; + this.algorithm = algorithm; + } + + @override + Future decrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + expect(data, this.data); + expect(key, this.key); + expect(algorithm, this.algorithm); + return response?.call(); + } + + void setDecryptAsListExpectations({ + required List data, + required Uint8List key, + required String algorithm, + }) { + dataAsList = data; + this.key = key; + this.algorithm = algorithm; + } + + @override + Future decryptAsList( + List data, + Uint8List key, + String algorithm, + ) async { + expect(data, dataAsList); + expect(key, this.key); + expect(algorithm, this.algorithm); + + return response?.call(); + } + + void setDigestExpectations({ + required Uint8List data, + required String algorithm, + }) { + this.data = data; + this.algorithm = algorithm; + } + + @override + Future digest(Uint8List data, String algorithm) async { + expect(data, this.data); + expect(algorithm, this.algorithm); + + return response?.call(); + } + + void setEncryptExpectations({ + required Uint8List data, + required Uint8List key, + required String algorithm, + }) { + this.data = data; + this.key = key; + this.algorithm = algorithm; + } + + @override + Future encrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + expect(data, this.data); + expect(key, this.key); + expect(algorithm, this.algorithm); + + return response?.call(); + } + + void setEncryptAsListExpectations({ + required Uint8List data, + required Uint8List key, + required String algorithm, + }) => + setEncryptExpectations( + data: data, + key: key, + algorithm: algorithm, + ); + + @override + Future?> encryptAsList( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + expect(data, this.data); + expect(key, this.key); + expect(algorithm, this.algorithm); + + return responseAsList?.call(); + } + + // ignore: use_setters_to_change_properties + void setGenerateKeyExpectations({required int bitsCount}) { + this.bitsCount = bitsCount; + } + + @override + Future generateSecretKey(int bitsCount) async { + expect(bitsCount, this.bitsCount); + + return response?.call(); + } + + void setPbkdf2Expectations({ + required String password, + required String salt, + required int keyBytesCount, + required int iterations, + required String algorithm, + }) { + this.password = password; + this.salt = salt; + this.iterations = iterations; + this.keyBytesCount = keyBytesCount; + this.algorithm = algorithm; + } + + @override + Future pbkdf2( + String password, + String salt, + int keyBytesCount, + int iterations, + String algorithm, + ) async { + expect(password, this.password); + expect(salt, this.salt); + expect(keyBytesCount, this.keyBytesCount); + expect(iterations, this.iterations); + expect(algorithm, this.algorithm); + + return response?.call(); + } +} diff --git a/packages/native_crypto/test/src/aes_cipher_test.dart b/packages/native_crypto/test/src/aes_cipher_test.dart new file mode 100644 index 0000000..f1d0f39 --- /dev/null +++ b/packages/native_crypto/test/src/aes_cipher_test.dart @@ -0,0 +1,323 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: aes_cipher_test.dart +// Created Date: 26/05/2022 23:20:53 +// Last Modified: 27/05/2022 16:39:44 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +import '../mocks/mock_native_crypto_platform.dart'; + +void main() { + final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); + NativeCryptoPlatform.instance = mock; + + setUp(() { + Cipher.bytesCountPerChunk = Cipher.defaultBytesCountPerChunk; + }); + + group('Constructor', () { + test('throws on invalid key length', () { + expect( + () => AES(SecretKey(Uint8List(0))), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_key_length', + ) + .having( + (e) => e.message, + 'message', + contains('Invalid key'), + ), + ), + ); + }); + + test('creates a valid instance', () { + expect( + AES( + SecretKey(Uint8List(16)), + ), + isA(), + ); + }); + }); + + group('encrypt', () { + test('returns a valid cipher text wrapper', () async { + mock + ..setEncryptExpectations( + data: Uint8List(16), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => Uint8List(16 + 28)); + + final aes = AES(SecretKey(Uint8List(16))); + + expect( + await aes.encrypt(Uint8List(16)), + isA().having((e) => e.isSingle, 'is single', isTrue), + ); + }); + + test('returns a valid cipher text with multiple chunks', () async { + mock + ..setEncryptExpectations( + data: Uint8List(16), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => Uint8List(16 + 28)); // Returns 1 encrypted chunk + Cipher.bytesCountPerChunk = 16; + final aes = AES(SecretKey(Uint8List(16))); + + expect( + await aes.encrypt(Uint8List(16 * 3)), + isA().having((e) => e.isList, 'is list', isTrue), + ); + }); + + test('handles returning empty list', () async { + mock + ..setEncryptExpectations( + data: Uint8List(16), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => Uint8List(0)); + + final aes = AES(SecretKey(Uint8List(16))); + + await expectLater( + () => aes.encrypt(Uint8List(16)), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_empty_data', + ), + ), + ); + }); + + test('handles returning null', () async { + mock + ..setEncryptExpectations( + data: Uint8List(16), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => null); + + final aes = AES(SecretKey(Uint8List(16))); + + await expectLater( + () => aes.encrypt(Uint8List(16)), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles throwing PlatformException', () async { + mock + ..setEncryptExpectations( + data: Uint8List(16), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse( + () => throw PlatformException( + code: 'native_crypto', + message: 'dummy error', + ), + ); + + final aes = AES(SecretKey(Uint8List(16))); + + await expectLater( + () => aes.encrypt(Uint8List(16)), + throwsA( + isA() + .having( + (e) => e.message, + 'message', + contains( + 'PlatformException(native_crypto, dummy error, null, null)', + ), + ) + .having( + (e) => e.code, + 'code', + 'platform_throws', + ), + ), + ); + }); + }); + + group('decrypt', () { + test('returns a valid Uint8List', () async { + mock + ..setDecryptExpectations( + data: Uint8List(16 + 28), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => Uint8List(16)); + + final aes = AES(SecretKey(Uint8List(16))); + final bytes = Uint8List(16 + 28); + final wrapper = CipherTextWrapper.fromBytes( + bytes, + ivLength: 12, + tagLength: 16, + ); + + expect( + await aes.decrypt(wrapper), + isA().having((e) => e.length, 'length', 16), + ); + }); + + test('returns a valid Uint8List on decrypting multiple chunks', () async { + const int chunkSize = 8; + mock + ..setDecryptExpectations( + data: Uint8List(chunkSize + 28), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => Uint8List(chunkSize)); + Cipher.bytesCountPerChunk = chunkSize; + final aes = AES(SecretKey(Uint8List(16))); + final bytes = Uint8List((chunkSize + 28) * 3); + final wrapper = CipherTextWrapper.fromBytes( + bytes, + ivLength: 12, + tagLength: 16, + ); + + expect( + await aes.decrypt(wrapper), + isA().having((e) => e.length, 'length', chunkSize * 3), + ); + }); + + test('handles returning empty list', () async { + mock + ..setDecryptExpectations( + data: Uint8List(16 + 28), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => Uint8List(0)); + + final aes = AES(SecretKey(Uint8List(16))); + final bytes = Uint8List(16 + 28); + final wrapper = CipherTextWrapper.fromBytes( + bytes, + ivLength: 12, + tagLength: 16, + ); + + await expectLater( + () => aes.decrypt(wrapper), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_empty_data', + ), + ), + ); + }); + + test('handles returning null', () async { + mock + ..setDecryptExpectations( + data: Uint8List(16 + 28), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse(() => null); + + final aes = AES(SecretKey(Uint8List(16))); + final bytes = Uint8List(16 + 28); + final wrapper = CipherTextWrapper.fromBytes( + bytes, + ivLength: 12, + tagLength: 16, + ); + + await expectLater( + () => aes.decrypt(wrapper), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles throwing PlatformException', () async { + mock + ..setDecryptExpectations( + data: Uint8List(16 + 28), + key: Uint8List(16), + algorithm: 'aes', + ) + ..setResponse( + () => throw PlatformException( + code: 'native_crypto', + message: 'dummy error', + ), + ); + + final aes = AES(SecretKey(Uint8List(16))); + final bytes = Uint8List(16 + 28); + final wrapper = CipherTextWrapper.fromBytes( + bytes, + ivLength: 12, + tagLength: 16, + ); + + await expectLater( + () => aes.decrypt(wrapper), + throwsA( + isA() + .having( + (e) => e.message, + 'message', + contains( + 'PlatformException(native_crypto, dummy error, null, null)', + ), + ) + .having( + (e) => e.code, + 'code', + 'platform_throws', + ), + ), + ); + }); + }); +} diff --git a/packages/native_crypto/test/src/cipher_text_test.dart b/packages/native_crypto/test/src/cipher_text_test.dart new file mode 100644 index 0000000..16d98a5 --- /dev/null +++ b/packages/native_crypto/test/src/cipher_text_test.dart @@ -0,0 +1,192 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: cipher_text_test.dart +// Created Date: 26/05/2022 20:45:38 +// Last Modified: 26/05/2022 21:29:51 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/native_crypto.dart'; + +void main() { + setUp(() { + Cipher.bytesCountPerChunk = Cipher.defaultBytesCountPerChunk; + }); + + group('fromBytes', () { + test('throws if length is not the one expected', () { + final Uint8List bytes = Uint8List.fromList([1, 2, 3, 4, 5]); + expect( + () => CipherText.fromBytes( + bytes, + ivLength: 1, + messageLength: 1, + tagLength: 1, + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('Invalid cipher text length'), + ), + ), + ); + }); + + test('throws if length is bigger than expected', () { + final Uint8List bytes = Uint8List.fromList([1, 3, 3, 3, 1]); + Cipher.bytesCountPerChunk = 2; + expect( + () => CipherText.fromBytes( + bytes, + ivLength: 1, + messageLength: 3, + tagLength: 1, + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('Cipher text is too big'), + ), + ), + ); + }); + + test('throws if data is empty', () { + final Uint8List bytes = Uint8List(0); + expect( + () => CipherText.fromBytes( + bytes, + ivLength: 1, + messageLength: 3, + tagLength: 1, + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('Passed data is empty'), + ), + ), + ); + }); + + test('throws if one of the length is negative', () { + final Uint8List bytes = Uint8List(0); + expect( + () => CipherText.fromBytes( + bytes, + ivLength: -1, + messageLength: 1, + tagLength: 1, + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('Invalid length'), + ), + ), + ); + }); + }); + + group('get.cipherAlgorithm', () { + test('throws if not set', () { + final CipherText cipherText = CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ); + expect( + () => cipherText.cipherAlgorithm, + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_cipher', + ) + .having( + (e) => e.message, + 'message', + contains('Cipher algorithm is not specified'), + ), + ), + ); + }); + + test('returns the expected value', () { + final CipherText cipherText = CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + cipherAlgorithm: CipherAlgorithm.aes, + ); + expect(cipherText.cipherAlgorithm, CipherAlgorithm.aes); + }); + }); + + group('Lengths', () { + test('get.ivLength returns the expected value', () { + final CipherText cipherText = CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ); + expect(cipherText.ivLength, 1); + }); + + test('get.messageLength returns the expected value', () { + final CipherText cipherText = CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ); + expect(cipherText.messageLength, 1); + }); + + test('get.tagLength returns the expected value', () { + final CipherText cipherText = CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ); + expect(cipherText.tagLength, 1); + }); + }); +} diff --git a/packages/native_crypto/test/src/cipher_text_wrapper_test.dart b/packages/native_crypto/test/src/cipher_text_wrapper_test.dart new file mode 100644 index 0000000..8dc0116 --- /dev/null +++ b/packages/native_crypto/test/src/cipher_text_wrapper_test.dart @@ -0,0 +1,349 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: cipher_text_wrapper_test.dart +// Created Date: 26/05/2022 21:35:41 +// Last Modified: 27/05/2022 13:46:54 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/native_crypto.dart'; + +void main() { + late CipherText single; + late List list; + + setUp(() { + Cipher.bytesCountPerChunk = Cipher.defaultBytesCountPerChunk; + single = CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ); + list = [ + CipherText.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ), + CipherText.fromBytes( + Uint8List.fromList([4, 5, 6]), + ivLength: 1, + messageLength: 1, + tagLength: 1, + ), + ]; + }); + + group('single', () { + test('makes isSingle true', () { + final wrapper = CipherTextWrapper.single(single); + expect(wrapper.isSingle, isTrue); + }); + + test('makes isList false', () { + final wrapper = CipherTextWrapper.single(single); + expect(wrapper.isList, isFalse); + }); + + test('makes CipherText the single value', () { + final wrapper = CipherTextWrapper.single(single); + expect(wrapper.single, single); + }); + + test('throws when trying to get list', () { + final wrapper = CipherTextWrapper.single(single); + expect( + () => wrapper.list, + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('is not list'), + ), + ), + ); + }); + + test('makes wrapper returns bytes of CipherText', () { + final wrapper = CipherTextWrapper.single(single); + expect(wrapper.bytes, single.bytes); + }); + + test('makes chunkCount = 1', () { + final wrapper = CipherTextWrapper.single(single); + expect(wrapper.chunkCount, 1); + }); + + test('makes unwrap() returns only CipherText', () { + final wrapper = CipherTextWrapper.single(single); + expect(wrapper.unwrap(), single); + }); + + test('makes unwrap() throws when trying to unwrap List', () { + final wrapper = CipherTextWrapper.single(single); + expect( + () => wrapper.unwrap>(), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('you should use unwrap'), + ), + ), + ); + }); + + test('makes adding is not supported', () { + final wrapper = CipherTextWrapper.single(single); + expect( + () => wrapper.add(single), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('is already single'), + ), + ), + ); + }); + }); + + group('list', () { + test('makes isList true', () { + final wrapper = CipherTextWrapper.list(list); + expect(wrapper.isList, isTrue); + }); + + test('makes isSingle false', () { + final wrapper = CipherTextWrapper.list(list); + expect(wrapper.isSingle, isFalse); + }); + + test('makes List the list value', () { + final wrapper = CipherTextWrapper.list(list); + expect(wrapper.list, list); + }); + + test('throws when trying to get single', () { + final wrapper = CipherTextWrapper.list(list); + expect( + () => wrapper.single, + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('is not single'), + ), + ), + ); + }); + + test('makes wrapper returns bytes of all CipherText joined', () { + final wrapper = CipherTextWrapper.list(list); + expect(wrapper.bytes, Uint8List.fromList([1, 2, 3, 4, 5, 6])); + }); + + test('makes chunkCount = 2', () { + final wrapper = CipherTextWrapper.list(list); + expect(wrapper.chunkCount, 2); + }); + + test('makes unwrap() returns List', () { + final wrapper = CipherTextWrapper.list(list); + expect(wrapper.unwrap>(), list); + }); + + test('makes unwrap() throws when trying to unwrap single', () { + final wrapper = CipherTextWrapper.list(list); + expect( + () => wrapper.unwrap(), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('you should use unwrap'), + ), + ), + ); + }); + + test('makes adding is supported', () { + final originalList = List.from(list); + final wrapper = CipherTextWrapper.list(list)..add(single); + printOnFailure(list.length.toString()); + expect(wrapper.list, [...originalList, single]); + }); + }); + + group('empty', () { + test('makes isList true', () { + final wrapper = CipherTextWrapper.empty(); + expect(wrapper.isList, isTrue); + }); + + test('makes isSingle false', () { + final wrapper = CipherTextWrapper.empty(); + expect(wrapper.isSingle, isFalse); + }); + + test('makes List the list value', () { + final wrapper = CipherTextWrapper.empty(); + expect(wrapper.list, []); + }); + + test('throws when trying to get single', () { + final wrapper = CipherTextWrapper.empty(); + expect( + () => wrapper.single, + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('is not single'), + ), + ), + ); + }); + + test('makes wrapper returns empty bytes', () { + final wrapper = CipherTextWrapper.empty(); + expect(wrapper.bytes, Uint8List.fromList([])); + }); + + test('makes chunkCount = 0', () { + final wrapper = CipherTextWrapper.empty(); + expect(wrapper.chunkCount, 0); + }); + + test('makes unwrap() returns empty List', () { + final wrapper = CipherTextWrapper.empty(); + expect(wrapper.unwrap>(), []); + }); + + test('makes unwrap() throws when trying to unwrap single', () { + final wrapper = CipherTextWrapper.empty(); + expect( + () => wrapper.unwrap(), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_data', + ) + .having( + (e) => e.message, + 'message', + contains('you should use unwrap'), + ), + ), + ); + }); + + test('makes adding is supported', () { + final wrapper = CipherTextWrapper.empty()..add(single); + expect(wrapper.list, [single]); + }); + }); + + group('fromBytes', () { + test('creates single from bytes when no too big', () { + final wrapper = CipherTextWrapper.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + tagLength: 1, + ); + expect(wrapper.isSingle, isTrue); + expect(wrapper.single, single); + }); + + test('creates list from bytes when too big', () { + Cipher.bytesCountPerChunk = 1; + final wrapper = CipherTextWrapper.fromBytes( + Uint8List.fromList([1, 2, 3, 4, 5, 6]), + ivLength: 1, + tagLength: 1, + ); + expect(wrapper.isList, isTrue); + expect(wrapper.list, list); + }); + + test('modifies Cipher.bytesCountPerChunk', () { + expect(Cipher.bytesCountPerChunk, Cipher.defaultBytesCountPerChunk); + CipherTextWrapper.fromBytes( + Uint8List.fromList([1, 2, 3]), + ivLength: 1, + tagLength: 1, + chunkSize: 3, + ); + expect(Cipher.bytesCountPerChunk, 3); + }); + + test('throws if trying to build list with bad parameters', () { + Cipher.bytesCountPerChunk = 1; // length of a message + + expect( + () => CipherTextWrapper.fromBytes( + Uint8List.fromList([1, 2, 3, 4, 5, 6]), + ivLength: 2, + tagLength: 1, + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('on chunk #'), + ), + ), + ); + }); + }); +} diff --git a/packages/native_crypto/test/src/hash_algorithm_test.dart b/packages/native_crypto/test/src/hash_algorithm_test.dart new file mode 100644 index 0000000..b7911b1 --- /dev/null +++ b/packages/native_crypto/test/src/hash_algorithm_test.dart @@ -0,0 +1,128 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: hash_algorithm_test.dart +// Created Date: 26/05/2022 22:28:53 +// Last Modified: 26/05/2022 23:03:03 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/src/utils/hash_algorithm.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +import '../mocks/mock_native_crypto_platform.dart'; + +void main() { + final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); + NativeCryptoPlatform.instance = mock; + + group('name', () { + test('is sha256 for HashAlgorithm.sha256', () { + expect(HashAlgorithm.sha256.name, 'sha256'); + }); + test('is sha384 for HashAlgorithm.sha384', () { + expect(HashAlgorithm.sha384.name, 'sha384'); + }); + test('is sha512 for HashAlgorithm.sha512', () { + expect(HashAlgorithm.sha512.name, 'sha512'); + }); + }); + + group('digest', () { + test('handles returning empty list', () async { + mock + ..setDigestExpectations( + data: Uint8List.fromList([1, 2, 3]), + algorithm: 'sha256', + ) + ..setResponse(() => Uint8List(0)); + + await expectLater( + () => HashAlgorithm.sha256.digest(Uint8List.fromList([1, 2, 3])), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_empty_data', + ), + ), + ); + }); + + test('handles returning null', () async { + mock + ..setDigestExpectations( + data: Uint8List.fromList([1, 2, 3]), + algorithm: 'sha256', + ) + ..setResponse(() => null); + + await expectLater( + () => HashAlgorithm.sha256.digest(Uint8List.fromList([1, 2, 3])), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles throwing PlatformException', () async { + mock + ..setDigestExpectations( + data: Uint8List.fromList([1, 2, 3]), + algorithm: 'sha256', + ) + ..setResponse( + () => throw PlatformException( + code: 'native_crypto', + message: 'dummy error', + ), + ); + + await expectLater( + () => HashAlgorithm.sha256.digest(Uint8List.fromList([1, 2, 3])), + throwsA( + isA() + .having( + (e) => e.message, + 'message', + 'PlatformException(native_crypto, dummy error, null, null)', + ) + .having( + (e) => e.code, + 'code', + 'platform_throws', + ), + ), + ); + }); + + test('returns data on success', () async { + final hash = Uint8List.fromList([4, 5, 6]); + mock + ..setDigestExpectations( + data: Uint8List.fromList([1, 2, 3]), + algorithm: 'sha256', + ) + ..setResponse(() => hash); + + final result = await HashAlgorithm.sha256.digest( + Uint8List.fromList( + [1, 2, 3], + ), + ); + + expect( + result, + hash, + ); + }); + }); +} diff --git a/packages/native_crypto/test/src/pbkdf2_test.dart b/packages/native_crypto/test/src/pbkdf2_test.dart new file mode 100644 index 0000000..f0ae084 --- /dev/null +++ b/packages/native_crypto/test/src/pbkdf2_test.dart @@ -0,0 +1,280 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: pbkdf2_test.dart +// Created Date: 26/05/2022 22:37:27 +// Last Modified: 26/05/2022 23:20:11 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +import '../mocks/mock_native_crypto_platform.dart'; + +void main() { + final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); + NativeCryptoPlatform.instance = mock; + + group('Constructor', () { + test('throws if keyBytesCount is negative', () { + expect( + () => Pbkdf2(keyBytesCount: -1, iterations: 10000), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('must be positive'), + ), + ), + ); + }); + + test('throws if iterations is negative or 0', () { + expect( + () => Pbkdf2(keyBytesCount: 32, iterations: -1), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('must be strictly positive'), + ), + ), + ); + }); + }); + + group('derive', () { + test('throws if password is null', () async { + final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); + await expectLater( + () => pbkdf2.derive( + salt: 'salt', + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('cannot be null'), + ), + ), + ); + }); + + test('throws if salt is null', () async { + final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); + await expectLater( + () => pbkdf2.derive( + password: 'password', + ), + throwsA( + isA() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('cannot be null'), + ), + ), + ); + }); + + test('handles returning empty list', () async { + mock + ..setPbkdf2Expectations( + password: 'password', + salt: 'salt', + keyBytesCount: 32, + iterations: 10000, + algorithm: 'sha256', + ) + ..setResponse(() => Uint8List(0)); + + final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); + + await expectLater( + () => pbkdf2.derive( + password: 'password', + salt: 'salt', + ), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_empty_data', + ), + ), + ); + }); + + test('handles returning null', () async { + mock + ..setPbkdf2Expectations( + password: 'password', + salt: 'salt', + keyBytesCount: 32, + iterations: 10000, + algorithm: 'sha256', + ) + ..setResponse(() => null); + + final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); + + await expectLater( + () => pbkdf2.derive( + password: 'password', + salt: 'salt', + ), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles returning data with wrong length', () async { + mock + ..setPbkdf2Expectations( + password: 'password', + salt: 'salt', + keyBytesCount: 32, + iterations: 10000, + algorithm: 'sha256', + ) + ..setResponse(() => Uint8List(33)); + + final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); + + await expectLater( + () => pbkdf2.derive( + password: 'password', + salt: 'salt', + ), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_invalid_data', + ), + ), + ); + }); + + test('handles throwing PlatformException', () async { + mock + ..setPbkdf2Expectations( + password: 'password', + salt: 'salt', + keyBytesCount: 32, + iterations: 10000, + algorithm: 'sha256', + ) + ..setResponse( + () => throw PlatformException( + code: 'native_crypto', + message: 'dummy error', + ), + ); + + final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); + + await expectLater( + () => pbkdf2.derive( + password: 'password', + salt: 'salt', + ), + throwsA( + isA() + .having( + (e) => e.message, + 'message', + 'PlatformException(native_crypto, dummy error, null, null)', + ) + .having( + (e) => e.code, + 'code', + 'platform_throws', + ), + ), + ); + }); + + test('returns SecretKey on success', () async { + final data = Uint8List.fromList([1, 2, 3, 4, 5, 6]); + final sk = SecretKey(data); + mock + ..setPbkdf2Expectations( + password: 'password', + salt: 'salt', + keyBytesCount: 6, + iterations: 10000, + algorithm: 'sha256', + ) + ..setResponse(() => data); + + final pbkdf = Pbkdf2(keyBytesCount: 6, iterations: 10000); + final result = await pbkdf.derive( + password: 'password', + salt: 'salt', + ); + + expect( + result, + sk, + ); + }); + + test('return empty SecretKey when keyBytesCount is set to 0', () async { + final sk = SecretKey(Uint8List(0)); + mock + ..setPbkdf2Expectations( + password: 'password', + salt: 'salt', + keyBytesCount: 0, + iterations: 10000, + algorithm: 'sha256', + ) + ..setResponse(() => Uint8List(0)); + + final pbkdf = Pbkdf2(keyBytesCount: 0, iterations: 10000); + final result = await pbkdf.derive( + password: 'password', + salt: 'salt', + ); + + expect( + result, + sk, + ); + }); + }); +} diff --git a/packages/native_crypto/test/src/secret_key_test.dart b/packages/native_crypto/test/src/secret_key_test.dart new file mode 100644 index 0000000..df2f159 --- /dev/null +++ b/packages/native_crypto/test/src/secret_key_test.dart @@ -0,0 +1,125 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: secret_key_test.dart +// Created Date: 26/05/2022 10:52:41 +// Last Modified: 26/05/2022 22:38:07 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/src/keys/secret_key.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +import '../mocks/mock_native_crypto_platform.dart'; + +void main() { + final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); + NativeCryptoPlatform.instance = mock; + + group('Constructors', () { + test('handles Uint8List', () { + final SecretKey key = SecretKey(Uint8List.fromList([1, 2, 3, 4, 5])); + + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); + }); + + test('handles base16', () { + final SecretKey key = SecretKey.fromBase16('0102030405'); + + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); + }); + + test('handles base64', () { + final SecretKey key = SecretKey.fromBase64('AQIDBAU='); + + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); + }); + + test('handles utf8', () { + final SecretKey key = SecretKey.fromUtf8('ABCDE'); + + expect(key.bytes, Uint8List.fromList([65, 66, 67, 68, 69])); + }); + }); + + group('fromSecureRandom', () { + test('handles returning random bytes', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse(() => Uint8List.fromList([1, 2, 3, 4, 5])); + + final SecretKey secretKey = await SecretKey.fromSecureRandom(5); + + expect( + secretKey.bytes, + Uint8List.fromList([1, 2, 3, 4, 5]), + ); + }); + + test('handles returning empty list', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse(() => Uint8List(0)); + + await expectLater( + () => SecretKey.fromSecureRandom(5), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_empty_data', + ), + ), + ); + }); + + test('handles returning null', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse(() => null); + + await expectLater( + () => SecretKey.fromSecureRandom(5), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles throwing PlatformException', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse( + () => throw PlatformException( + code: 'native_crypto', + message: 'dummy error', + ), + ); + + await expectLater( + () => SecretKey.fromSecureRandom(5), + throwsA( + isA() + .having( + (e) => e.message, + 'message', + 'PlatformException(native_crypto, dummy error, null, null)', + ) + .having( + (e) => e.code, + 'code', + 'platform_throws', + ), + ), + ); + }); + }); +} diff --git a/native_crypto_ios/example/.gitignore b/packages/native_crypto_android/.gitignore similarity index 53% rename from native_crypto_ios/example/.gitignore rename to packages/native_crypto_android/.gitignore index 0fa6b67..9be145f 100644 --- a/native_crypto_ios/example/.gitignore +++ b/packages/native_crypto_android/.gitignore @@ -21,26 +21,9 @@ #.vscode/ # Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock **/doc/api/ -**/ios/Flutter/.last_build_id .dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies .packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release +build/ diff --git a/native_crypto_ios/example/.metadata b/packages/native_crypto_android/.metadata similarity index 75% rename from native_crypto_ios/example/.metadata rename to packages/native_crypto_android/.metadata index ee7f61d..8c15ad7 100644 --- a/native_crypto_ios/example/.metadata +++ b/packages/native_crypto_android/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: cf4400006550b70f28e4b4af815151d1e74846c6 + revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b channel: stable -project_type: app +project_type: plugin diff --git a/packages/native_crypto_android/CHANGELOG.md b/packages/native_crypto_android/CHANGELOG.md new file mode 100644 index 0000000..ce1055b --- /dev/null +++ b/packages/native_crypto_android/CHANGELOG.md @@ -0,0 +1,41 @@ +## 0.1.1 + + - **REFACTOR**: clean and modernize kotlin code. + - **PERF**: x10 perfomance improvement on android with better list management. + - **FEAT**: export new exceptions. + +## 0.1.0 + +> Breaking changes ! + +* Follow **Federated Plugin** Flutter standard. + +## 0.0.6 + +* Add KeyPair generation. +* Rework exposed API. + +## 0.0.5 + +* New API. +* Add digest support. +* Clean platform specific code base. + +## 0.0.4 + +* Improve AES. + +## 0.0.3 + +* Add PBKDF2 support. +* Add exceptions. +* Improve documentation. + +## 0.0.2 + +* Add different key size support. +* Improve performances. + +## 0.0.1 + +* First AES cross-platform encryption & decryption implementation. diff --git a/packages/native_crypto_android/LICENSE b/packages/native_crypto_android/LICENSE new file mode 100644 index 0000000..dd5d33b --- /dev/null +++ b/packages/native_crypto_android/LICENSE @@ -0,0 +1,23 @@ +NativeCrypto - Android Implementation + +MIT License + +Copyright (c) 2019 - 2022 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +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 diff --git a/native_crypto_ios/README.md b/packages/native_crypto_android/README.md similarity index 94% rename from native_crypto_ios/README.md rename to packages/native_crypto_android/README.md index 302c828..48b93a5 100644 --- a/native_crypto_ios/README.md +++ b/packages/native_crypto_android/README.md @@ -1,4 +1,4 @@ -# native_crypto_ios +# native_crypto_android A new flutter plugin project. diff --git a/packages/native_crypto_android/android/.gitignore b/packages/native_crypto_android/android/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/packages/native_crypto_android/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/packages/native_crypto_android/android/build.gradle b/packages/native_crypto_android/android/build.gradle new file mode 100644 index 0000000..301735f --- /dev/null +++ b/packages/native_crypto_android/android/build.gradle @@ -0,0 +1,50 @@ +group 'fr.pointcheval.native_crypto_android' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.6.21' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 30 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + minSdkVersion 26 + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/packages/native_crypto_android/android/gradle/wrapper/gradle-wrapper.jar b/packages/native_crypto_android/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/packages/native_crypto_android/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/packages/native_crypto_android/android/gradle/wrapper/gradle-wrapper.properties b/packages/native_crypto_android/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..da9702f --- /dev/null +++ b/packages/native_crypto_android/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/native_crypto_android/android/gradlew b/packages/native_crypto_android/android/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/packages/native_crypto_android/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/packages/native_crypto_android/android/gradlew.bat b/packages/native_crypto_android/android/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/packages/native_crypto_android/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/packages/native_crypto_android/android/settings.gradle b/packages/native_crypto_android/android/settings.gradle new file mode 100644 index 0000000..3533b9f --- /dev/null +++ b/packages/native_crypto_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'native_crypto_android' diff --git a/packages/native_crypto_android/android/src/main/AndroidManifest.xml b/packages/native_crypto_android/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a750e9f --- /dev/null +++ b/packages/native_crypto_android/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt new file mode 100644 index 0000000..36cd412 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt @@ -0,0 +1,160 @@ +package fr.pointcheval.native_crypto_android + +import androidx.annotation.NonNull +import fr.pointcheval.native_crypto_android.interfaces.Cipher +import fr.pointcheval.native_crypto_android.kdf.Pbkdf2 +import fr.pointcheval.native_crypto_android.keys.SecretKey +import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm +import fr.pointcheval.native_crypto_android.utils.Constants +import fr.pointcheval.native_crypto_android.utils.HashAlgorithm +import fr.pointcheval.native_crypto_android.utils.Task +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result +import java.util.* + +/** NativeCryptoAndroidPlugin */ +class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel: MethodChannel + private val name = "plugins.hugop.cl/native_crypto" + + private var cipherInstance: Cipher? = null + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, name) + channel.setMethodCallHandler(this) + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + lateinit var methodCallTask: Task<*> + + when (call.method) { + "digest" -> methodCallTask = handleDigest(call.arguments()) + "generateSecretKey" -> methodCallTask = handleGenerateSecretKey(call.arguments()) + "pbkdf2" -> methodCallTask = handlePbkdf2(call.arguments()) + "encryptAsList" -> methodCallTask = handleEncryptAsList(call.arguments()) + "decryptAsList" -> methodCallTask = handleDecryptAsList(call.arguments()) + "encrypt" -> methodCallTask = handleCrypt(call.arguments(), true) + "decrypt" -> methodCallTask = handleCrypt(call.arguments(), false) + else -> result.notImplemented() + } + + methodCallTask.call() + + methodCallTask.finalize { task -> + if (task.isSuccessful()) { + result.success(task.getResult()) + } else { + val exception: Exception = task.getException() + val message = exception.message + result.error("native_crypto", message, null) + } + } + } + + private fun handleDigest(arguments: Map?): Task { + return Task { + val data: ByteArray = + Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray + val algorithm: String = + Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String + HashAlgorithm.digest(data, algorithm) + } + } + + private fun handleGenerateSecretKey(arguments: Map?): Task { + return Task { + val bitsCount: Int = Objects.requireNonNull(arguments?.get(Constants.BITS_COUNT)) as Int + SecretKey.fromSecureRandom(bitsCount).bytes + } + } + + private fun handlePbkdf2(arguments: Map?): Task { + return Task { + val password: String = + Objects.requireNonNull(arguments?.get(Constants.PASSWORD)) as String + val salt: String = Objects.requireNonNull(arguments?.get(Constants.SALT)) as String + val keyBytesCount: Int = + Objects.requireNonNull(arguments?.get(Constants.KEY_BYTES_COUNT)) as Int + val iterations: Int = + Objects.requireNonNull(arguments?.get(Constants.ITERATIONS)) as Int + val algorithm: String = + Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String + + val pbkdf2: Pbkdf2 = Pbkdf2(keyBytesCount, iterations, HashAlgorithm.valueOf(algorithm)) + pbkdf2.init(password, salt) + + pbkdf2.derive().bytes + } + } + + private fun lazyLoadCipher(cipherAlgorithm: CipherAlgorithm) { + if (cipherInstance == null) { + cipherInstance = cipherAlgorithm.getCipher() + } else { + if (cipherInstance!!.algorithm != cipherAlgorithm) { + cipherInstance = cipherAlgorithm.getCipher() + } + } + } + + private fun handleEncryptAsList(arguments: Map?): Task> { + return Task { + val data: ByteArray = + Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray + val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray + val algorithm: String = + Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String + + val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) + lazyLoadCipher(cipherAlgorithm) + + cipherInstance!!.encryptAsList(data, key) + } + } + + private fun handleDecryptAsList(arguments: Map?): Task { + return Task { + val data: List = + Objects.requireNonNull(arguments?.get(Constants.DATA)) as List + val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray + val algorithm: String = + Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String + + val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) + lazyLoadCipher(cipherAlgorithm) + + cipherInstance!!.decryptAsList(data, key) + } + } + + // **EN**Crypt and **DE**Crypt + private fun handleCrypt(arguments: Map?, forEncryption: Boolean): Task { + return Task { + val data: ByteArray = + Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray + val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray + val algorithm: String = + Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String + + val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) + lazyLoadCipher(cipherAlgorithm) + + if (forEncryption) { + cipherInstance!!.encrypt(data, key) + } else { + cipherInstance!!.decrypt(data, key) + } + } + } +} diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt new file mode 100644 index 0000000..0b4c86c --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt @@ -0,0 +1,55 @@ +package fr.pointcheval.native_crypto_android.ciphers + +import fr.pointcheval.native_crypto_android.interfaces.Cipher +import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +class AES : Cipher { + override val algorithm: CipherAlgorithm + get() = CipherAlgorithm.aes + + var cipherInstance: javax.crypto.Cipher? = null; + + fun lazyLoadCipher() { + if (cipherInstance == null) { + cipherInstance = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") + } + } + + // native.crypto cipherText representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] + // javax.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] + override fun encrypt(data: ByteArray, key: ByteArray): ByteArray { + val list : List = encryptAsList(data, key) + return list.first().plus(list.last()) + } + + // native.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] + // javax.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] + override fun encryptAsList(data: ByteArray, key: ByteArray): List { + val sk = SecretKeySpec(key, "AES") + lazyLoadCipher() + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + val bytes: ByteArray = cipherInstance!!.doFinal(data) + val iv: ByteArray = cipherInstance!!.iv + return listOf(iv, bytes) + } + + override fun decrypt(data: ByteArray, key: ByteArray): ByteArray { + val iv: ByteArray = data.take(12).toByteArray() + val payload: ByteArray = data.drop(12).toByteArray() + return decryptAsList(listOf(iv, payload), key) + } + + override fun decryptAsList(data: List, key: ByteArray): ByteArray { + val sk = SecretKeySpec(key, "AES") + val payload: ByteArray = data.last() + val iv: ByteArray = data.first() + val gcmSpec = GCMParameterSpec(16 * 8, iv) + lazyLoadCipher() + cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmSpec) + return cipherInstance!!.doFinal(payload) + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt new file mode 100644 index 0000000..5893d25 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt @@ -0,0 +1,12 @@ +package fr.pointcheval.native_crypto_android.interfaces + +import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm + +interface Cipher { + val algorithm: CipherAlgorithm + + fun encrypt(data: ByteArray, key: ByteArray): ByteArray + fun decrypt(data: ByteArray, key: ByteArray): ByteArray + fun encryptAsList(data: ByteArray, key: ByteArray): List + fun decryptAsList(data: List, key: ByteArray): ByteArray +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt new file mode 100644 index 0000000..4df212b --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt @@ -0,0 +1,5 @@ +package fr.pointcheval.native_crypto_android.interfaces + +interface Key { + val bytes: ByteArray +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt new file mode 100644 index 0000000..6bd9216 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt @@ -0,0 +1,10 @@ +package fr.pointcheval.native_crypto_android.interfaces + +import fr.pointcheval.native_crypto_android.keys.SecretKey +import fr.pointcheval.native_crypto_android.utils.KdfAlgorithm + +interface KeyDerivation { + val algorithm: KdfAlgorithm + + fun derive(): SecretKey +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt new file mode 100644 index 0000000..17d8c5e --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt @@ -0,0 +1,39 @@ +package fr.pointcheval.native_crypto_android.kdf + +import fr.pointcheval.native_crypto_android.interfaces.KeyDerivation +import fr.pointcheval.native_crypto_android.keys.SecretKey +import fr.pointcheval.native_crypto_android.utils.HashAlgorithm +import fr.pointcheval.native_crypto_android.utils.KdfAlgorithm +import javax.crypto.SecretKeyFactory +import javax.crypto.spec.PBEKeySpec + +class Pbkdf2( + private val keyBytesCount: Int, private val iterations: Int, + private val hash: HashAlgorithm = HashAlgorithm.sha256 +) : KeyDerivation { + + private var password: String? = null + private var salt: String? = null + + fun init(password: String, salt: String) { + this.password = password + this.salt = salt + } + + override val algorithm: KdfAlgorithm + get() = KdfAlgorithm.pbkdf2 + + override fun derive(): SecretKey { + if (password == null || salt == null) { + throw Exception("Password and Salt must be initialized.") + } + val spec = PBEKeySpec( + password!!.toCharArray(), + salt!!.toByteArray(), + iterations, + keyBytesCount * 8 + ) + val skf: SecretKeyFactory = SecretKeyFactory.getInstance(hash.pbkdf2String()) + return SecretKey(skf.generateSecret(spec).encoded) + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt new file mode 100644 index 0000000..07e9833 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt @@ -0,0 +1,14 @@ +package fr.pointcheval.native_crypto_android.keys + +import fr.pointcheval.native_crypto_android.interfaces.Key +import java.security.SecureRandom + +class SecretKey(override val bytes: ByteArray) : Key { + companion object { + fun fromSecureRandom(bitsCount: Int): SecretKey { + val bytes = ByteArray(bitsCount / 8) + SecureRandom.getInstanceStrong().nextBytes(bytes) + return SecretKey(bytes) + } + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt new file mode 100644 index 0000000..a95185f --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt @@ -0,0 +1,14 @@ +package fr.pointcheval.native_crypto_android.utils + +import fr.pointcheval.native_crypto_android.ciphers.AES +import fr.pointcheval.native_crypto_android.interfaces.Cipher + +enum class CipherAlgorithm { + aes; + + fun getCipher(): Cipher { + return when (this) { + aes -> AES() + } + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt new file mode 100644 index 0000000..1cb359a --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt @@ -0,0 +1,14 @@ +package fr.pointcheval.native_crypto_android.utils + +object Constants { + const val ALGORITHM = "algorithm" + const val BITS_COUNT = "bitsCount" + const val DATA = "data" + const val PASSWORD = "password" + const val SALT = "salt" + const val KEY = "key" + const val KEY_BYTES_COUNT = "keyBytesCount" + const val ITERATIONS = "iterations" + const val EPHEMERAL_PRIVATE_KEY = "ephemeralPrivateKey" + const val OTHER_PUBLIC_KEY = "otherPublicKey" +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt new file mode 100644 index 0000000..904f7ce --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt @@ -0,0 +1,50 @@ +package fr.pointcheval.native_crypto_android.utils + +import java.security.MessageDigest + +@Suppress("EnumEntryName") +enum class HashAlgorithm(val bitsCount: Int) { + sha256(256), + sha384(384), + sha512(512); + + fun messageDigestString(): String { + return when (this) { + sha256 -> "SHA-256" + sha384 -> "SHA-384" + sha512 -> "SHA-512" + } + } + + fun hmacString(): String { + return when (this) { + sha256 -> "HmacSHA256" + sha384 -> "HmacSHA384" + sha512 -> "HmacSHA512" + } + } + + fun pbkdf2String(): String { + return when (this) { + sha256 -> "PBKDF2WithHmacSHA256" + sha384 -> "PBKDF2WithHmacSHA384" + sha512 -> "PBKDF2WithHmacSHA512" + } + } + + fun digest(data: ByteArray): ByteArray { + val md = MessageDigest.getInstance(messageDigestString()) + return md.digest(data) + } + + companion object { + fun digest(data: ByteArray, algorithm: String): ByteArray { + for (h in values()) { + if (h.name == algorithm) { + return h.digest(data) + } + } + throw Exception("Unknown HashAlgorithm: $algorithm") + } + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt new file mode 100644 index 0000000..4ef9f69 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt @@ -0,0 +1,5 @@ +package fr.pointcheval.native_crypto_android.utils + +enum class KdfAlgorithm { + pbkdf2 +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt new file mode 100644 index 0000000..d4832de --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt @@ -0,0 +1,44 @@ +package fr.pointcheval.native_crypto_android.utils + +class Task(private var task: () -> T) { + + private var successful = false + private var result: T? = null + private var exception: Exception? = null + + fun isSuccessful(): Boolean { + return successful + } + + fun getResult(): T { + if (successful && result != null) { + return result!! + } else { + throw Exception("No result found!") + } + } + + fun getException(): Exception { + if (exception != null) { + return exception!! + } else { + throw Exception("No exception found!") + } + } + + fun call() { + try { + result = task() + exception = null + successful = true + } catch (e: Exception) { + exception = e + result = null + successful = false + } + } + + fun finalize(callback: (task: Task) -> Unit) { + callback(this) + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/pubspec.yaml b/packages/native_crypto_android/pubspec.yaml new file mode 100644 index 0000000..8b40a04 --- /dev/null +++ b/packages/native_crypto_android/pubspec.yaml @@ -0,0 +1,23 @@ +name: native_crypto_android +description: Android implementation of NativeCrypto +version: 0.1.1 + +environment: + sdk: ">=2.15.1 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + plugin: + implements: native_crypto + platforms: + android: + package: fr.pointcheval.native_crypto_android + pluginClass: NativeCryptoAndroidPlugin \ No newline at end of file diff --git a/native_crypto_ios/.gitignore b/packages/native_crypto_ios/.gitignore similarity index 100% rename from native_crypto_ios/.gitignore rename to packages/native_crypto_ios/.gitignore diff --git a/native_crypto_ios/.metadata b/packages/native_crypto_ios/.metadata similarity index 100% rename from native_crypto_ios/.metadata rename to packages/native_crypto_ios/.metadata diff --git a/packages/native_crypto_ios/CHANGELOG.md b/packages/native_crypto_ios/CHANGELOG.md new file mode 100644 index 0000000..1511feb --- /dev/null +++ b/packages/native_crypto_ios/CHANGELOG.md @@ -0,0 +1,40 @@ +## 0.1.1 + + - **REFACTOR**: rework swift part. + - **PERF**: optimize swift code. + +## 0.1.0 + +> Breaking changes ! + +* Follow **Federated Plugin** Flutter standard. + +## 0.0.6 + +* Add KeyPair generation. +* Rework exposed API. + +## 0.0.5 + +* New API. +* Add digest support. +* Clean platform specific code base. + +## 0.0.4 + +* Improve AES. + +## 0.0.3 + +* Add PBKDF2 support. +* Add exceptions. +* Improve documentation. + +## 0.0.2 + +* Add different key size support. +* Improve performances. + +## 0.0.1 + +* First AES cross-platform encryption & decryption implementation. diff --git a/native_crypto_ios/LICENSE b/packages/native_crypto_ios/LICENSE similarity index 96% rename from native_crypto_ios/LICENSE rename to packages/native_crypto_ios/LICENSE index a9f90ec..8c5c1f7 100644 --- a/native_crypto_ios/LICENSE +++ b/packages/native_crypto_ios/LICENSE @@ -2,7 +2,7 @@ NativeCrypto - iOS Implementation MIT License -Copyright (c) 2021 Hugo Pointcheval +Copyright (c) 2019 - 2022 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 diff --git a/packages/native_crypto_ios/README.md b/packages/native_crypto_ios/README.md new file mode 100644 index 0000000..a60dd49 --- /dev/null +++ b/packages/native_crypto_ios/README.md @@ -0,0 +1,15 @@ +# NativeCrypto - iOS Implementation + +iOS Implementation of NativeCrypto Plugin. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/native_crypto_ios/ios/.gitignore b/packages/native_crypto_ios/ios/.gitignore similarity index 100% rename from native_crypto_ios/ios/.gitignore rename to packages/native_crypto_ios/ios/.gitignore diff --git a/native_crypto_ios/ios/Assets/.gitkeep b/packages/native_crypto_ios/ios/Assets/.gitkeep similarity index 100% rename from native_crypto_ios/ios/Assets/.gitkeep rename to packages/native_crypto_ios/ios/Assets/.gitkeep diff --git a/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h b/packages/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h similarity index 100% rename from native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h rename to packages/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h diff --git a/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m b/packages/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m similarity index 100% rename from native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m rename to packages/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m diff --git a/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift b/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift new file mode 100644 index 0000000..c9b1a84 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift @@ -0,0 +1,146 @@ +import Flutter +import UIKit + +@available(iOS 13.0, *) +public class SwiftNativeCryptoIosPlugin: NSObject, FlutterPlugin { + static let name: String = "plugins.hugop.cl/native_crypto" + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: name, binaryMessenger: registrar.messenger()) + let instance = SwiftNativeCryptoIosPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "digest": _call(task: handleDigest(call: call), result: result) + case "generateSecretKey": _call(task: handleGenerateSecretKey(call: call), result: result) + case "pbkdf2": _call(task: handlePbkdf2(call: call), result: result) + case "encryptAsList": _call(task: handleEncryptAsList(call: call), result: result) + case "decryptAsList": _call(task: handleDecryptAsList(call: call), result: result) + case "encrypt": _call(task: handleCrypt(call: call, forEncryption: true), result: result) + case "decrypt": _call(task: handleCrypt(call: call, forEncryption: false), result: result) + default: result(FlutterMethodNotImplemented) + } + } + + private func _call(task: Task, result: @escaping FlutterResult) { + task.call() + task.finalize(callback: {(task: Task) in + if (task.isSuccessful()) { + result(task.getResult()!) + } else { + let exception: Error = task.getException() + let message = exception.localizedDescription + result(FlutterError(code: "native_crypto", message: message, details: nil)) + } + }) + } + + private func handleDigest(call: FlutterMethodCall) -> Task { + return Task(task: { + let args : NSDictionary = call.arguments as! NSDictionary + + let data : Data = (args["data"] as! FlutterStandardTypedData).data + let algorithm : String = args["algorithm"] as! String + + return FlutterStandardTypedData.init(bytes: try HashAlgorithm.digest(data: data, algorithm: algorithm)) + }) + } + + private func handleGenerateSecretKey(call: FlutterMethodCall) -> Task { + return Task(task: { + let args : NSDictionary = call.arguments as! NSDictionary + + let bitsCount : NSNumber = args["bitsCount"] as! NSNumber + + return FlutterStandardTypedData.init(bytes: SecretKey(fromSecureRandom: bitsCount.intValue).bytes) + }) + } + + private func handlePbkdf2(call: FlutterMethodCall) -> Task { + return Task(task: { + let args : NSDictionary = call.arguments as! NSDictionary + + let password : String = args["password"] as! String + let salt : String = args["salt"] as! String + let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber + let iterations : NSNumber = args["iterations"] as! NSNumber + let algorithm : String = args["algorithm"] as! String + + let pbkdf2 : Pbkdf2 = Pbkdf2(keyBytesCount: keyBytesCount.intValue, iterations: iterations.intValue) + pbkdf2.hash = HashAlgorithm.init(rawValue: algorithm) ?? pbkdf2.hash + pbkdf2.initialize(password: password, salt: salt) + + return FlutterStandardTypedData.init(bytes: try pbkdf2.derive().bytes) + }) + } + + private func handleEncryptAsList(call: FlutterMethodCall) -> Task> { + return Task(task: { + let args : NSDictionary = call.arguments as! NSDictionary + + let data : Data = (args["data"] as! FlutterStandardTypedData).data + let key : Data = (args["key"] as! FlutterStandardTypedData).data + let algorithm : String = args["algorithm"] as! String + + let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) + var cipher : Cipher + if (cipherAlgorithm != nil) { + cipher = cipherAlgorithm!.getCipher + } else { + throw NativeCryptoError.cipherError + } + + return try cipher.encryptAsList(data: data, key: key) + }) + } + + private func handleDecryptAsList(call: FlutterMethodCall) -> Task { + return Task(task: { + let args : NSDictionary = call.arguments as! NSDictionary + + let data = args["data"] as! NSArray + let key : Data = (args["key"] as! FlutterStandardTypedData).data + let algorithm : String = args["algorithm"] as! String + + let iv = (data[0] as! FlutterStandardTypedData).data + let encrypted = (data[1] as! FlutterStandardTypedData).data + + let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) + var cipher : Cipher + if (cipherAlgorithm != nil) { + cipher = cipherAlgorithm!.getCipher + } else { + throw NativeCryptoError.cipherError + } + + return FlutterStandardTypedData.init(bytes: try cipher.decryptAsList(data: [iv, encrypted], key: key)) + }) + } + + private func handleCrypt(call: FlutterMethodCall, forEncryption: Bool) -> Task { + return Task(task: { + let args : NSDictionary = call.arguments as! NSDictionary + + let data : Data = (args["data"] as! FlutterStandardTypedData).data + let key : Data = (args["key"] as! FlutterStandardTypedData).data + let algorithm : String = args["algorithm"] as! String + + let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) + var cipher : Cipher + if (cipherAlgorithm != nil) { + cipher = cipherAlgorithm!.getCipher + } else { + throw NativeCryptoError.cipherError + } + + if (forEncryption) { + return FlutterStandardTypedData.init(bytes: try cipher.encrypt(data: data, key: key)) + } else { + return FlutterStandardTypedData.init(bytes: try cipher.decrypt(data: data, key: key)) + } + }) + } + +} diff --git a/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift b/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift new file mode 100644 index 0000000..2110ff5 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift @@ -0,0 +1,57 @@ +// +// AESCipher.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation +import CryptoKit + +class AESCipher : Cipher { + var algorithm: CipherAlgorithm = CipherAlgorithm.aes + + /// Encrypts plaintext with key using AES GCM + @available(iOS 13.0, *) + func encrypt(data: Data, key: Data) throws -> Data { + let symmetricKey : SymmetricKey = SymmetricKey.init(data: key) + let encrypted : AES.GCM.SealedBox? = try? AES.GCM.seal(data, using: symmetricKey) + + let encryptedData : Data? = encrypted?.combined + if (encryptedData == nil) { + throw NativeCryptoError.encryptionError + } + return encryptedData! + } + + /// Decrypts ciphertext with key using AES GCM + @available(iOS 13.0, *) + func decrypt(data: Data, key: Data) throws -> Data { + let symmetricKey = SymmetricKey.init(data: key) + let sealedBox = try? AES.GCM.SealedBox(combined: data) + if (sealedBox == nil) { return Data.init() } + let decryptedData = try? AES.GCM.open(sealedBox!, using: symmetricKey) + if (decryptedData == nil) { + throw NativeCryptoError.decryptionError + } + return decryptedData! + } + + func encryptAsList(data: Data, key: Data) throws -> [Data] { + let encryptedData = try encrypt(data: data, key: key) + + let iv = encryptedData.prefix(12) + let data = encryptedData.suffix(from: 12) + + return [iv, data] + } + + func decryptAsList(data: [Data], key: Data) throws -> Data { + var encryptedData = data.first! + let data = data.last! + encryptedData.append(data) + + let decryptedData = try decrypt(data: encryptedData, key: key) + return decryptedData + } +} diff --git a/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift b/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift new file mode 100644 index 0000000..8a34a1e --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift @@ -0,0 +1,63 @@ +// +// Pbkdf2.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation +import CommonCrypto + +class Pbkdf2 : KeyDerivation { + var algorithm: KdfAlgorithm = KdfAlgorithm.pbkdf2 + + var keyBytesCount: Int + var iterations: Int + var hash: HashAlgorithm = HashAlgorithm.HashSHA256 + + var password: String? = nil + var salt: String? = nil + + init(keyBytesCount: Int, iterations: Int) { + self.keyBytesCount = keyBytesCount + self.iterations = iterations + } + + func initialize(password: String, salt: String) { + self.password = password + self.salt = salt + } + + func derive() throws -> SecretKey { + if (password == nil || salt == nil) { + throw NativeCryptoError.pbkdf2Error + } + + let passwordData = password!.data(using: .utf8)! + let saltData = salt!.data(using: .utf8)! + + var derivedKeyData = Data(repeating: 0, count: keyBytesCount) + let localDerivedKeyData = derivedKeyData + + let status = derivedKeyData.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutableRawBufferPointer) in + saltData.withUnsafeBytes { (saltBytes: UnsafeRawBufferPointer) in + CCKeyDerivationPBKDF( + CCPBKDFAlgorithm(kCCPBKDF2), + password, + passwordData.count, + saltBytes.bindMemory(to: UInt8.self).baseAddress, + saltData.count, + hash.pbkdf2identifier, + UInt32(iterations), + derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress, + localDerivedKeyData.count) + } + } + + if (status != kCCSuccess) { + throw NativeCryptoError.pbkdf2Error + } + + return SecretKey(derivedKeyData) + } +} diff --git a/packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift b/packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift new file mode 100644 index 0000000..6325143 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift @@ -0,0 +1,25 @@ +// +// SecretKey.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation +import CryptoKit + +class SecretKey : Key { + var bytes: Data + + init(_ bytes: Data) { + self.bytes = bytes + } + + init(fromSecureRandom bitsCount: Int) { + let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: bitsCount)) + bytes = symmetricKey.withUnsafeBytes + { + return Data(Array($0)) + } + } +} diff --git a/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift b/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift new file mode 100644 index 0000000..f1291f9 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift @@ -0,0 +1,16 @@ +// +// Cipher.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +protocol Cipher { + var algorithm: CipherAlgorithm { get } + func encrypt(data: Data, key: Data) throws -> Data + func decrypt(data: Data, key: Data) throws -> Data + func encryptAsList(data: Data, key: Data) throws -> [Data] + func decryptAsList(data: [Data], key: Data) throws-> Data +} diff --git a/packages/native_crypto_ios/ios/Classes/protocols/Key.swift b/packages/native_crypto_ios/ios/Classes/protocols/Key.swift new file mode 100644 index 0000000..9fc9199 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/protocols/Key.swift @@ -0,0 +1,12 @@ +// +// Key.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +protocol Key { + var bytes: Data { get set } +} diff --git a/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift b/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift new file mode 100644 index 0000000..df3a0d5 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift @@ -0,0 +1,13 @@ +// +// KeyDerivation.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +protocol KeyDerivation { + var algorithm : KdfAlgorithm { get } + func derive() throws -> SecretKey +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift b/packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift new file mode 100644 index 0000000..afb9094 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift @@ -0,0 +1,18 @@ +// +// CipherAlgorithm.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +enum CipherAlgorithm : String { + case aes = "aes" + + var getCipher: Cipher { + switch self { + case .aes: return AESCipher() + } + } +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift new file mode 100644 index 0000000..38b7740 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift @@ -0,0 +1,45 @@ +// +// HashAlgorithm.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation +import CommonCrypto +import CryptoKit + +enum HashAlgorithm: String { + case HashSHA256 = "sha256" + case HashSHA384 = "sha384" + case HashSHA512 = "sha512" + + var pbkdf2identifier: UInt32 { + switch self { + case .HashSHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) + case .HashSHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) + case .HashSHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) + } + } + + @available(iOS 13.0, *) + func digest(data: Data) -> Data { + switch self { + case .HashSHA256: + return Data(SHA256.hash(data: data)) + case .HashSHA384: + return Data(SHA384.hash(data: data)) + case .HashSHA512: + return Data(SHA512.hash(data: data)) + } + } + + @available(iOS 13.0, *) + static func digest(data: Data, algorithm: String) throws -> Data { + let algo = HashAlgorithm.init(rawValue: algorithm) + if (algo == nil) { + throw NativeCryptoError.messageDigestError + } + return algo!.digest(data: data) + } +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift b/packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift new file mode 100644 index 0000000..d6af3c1 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift @@ -0,0 +1,12 @@ +// +// KdfAlgorithm.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +enum KdfAlgorithm { + case pbkdf2 +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift b/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift new file mode 100644 index 0000000..a2ac159 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift @@ -0,0 +1,18 @@ +// +// NativeCryptoError.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +enum NativeCryptoError : Error { +case decryptionError +case encryptionError +case messageDigestError +case pbkdf2Error +case cipherError +case resultError +case exceptionError +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/Task.swift b/packages/native_crypto_ios/ios/Classes/utils/Task.swift new file mode 100644 index 0000000..4d11070 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/Task.swift @@ -0,0 +1,52 @@ +// +// Task.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 25/05/2022. +// + +import Foundation + +class Task { + + var task: () throws -> T + private var successful: Bool = false + private var result: T? = nil + private var exception: Error? = nil + + init(task: @escaping () throws -> T) { + self.task = task + } + + func isSuccessful() -> Bool { + return successful + } + + func getResult() -> T? { + return result + } + + func getException() -> Error { + if (exception != nil) { + return exception! + } else { + return NativeCryptoError.exceptionError + } + } + + func call() { + do { + result = try task() + exception = nil + successful = true + } catch { + exception = error + result = nil + successful = false + } + } + + func finalize(callback: (_ task: Task) -> Void) { + callback(self) + } +} diff --git a/native_crypto_ios/ios/native_crypto_ios.podspec b/packages/native_crypto_ios/ios/native_crypto_ios.podspec similarity index 100% rename from native_crypto_ios/ios/native_crypto_ios.podspec rename to packages/native_crypto_ios/ios/native_crypto_ios.podspec diff --git a/native_crypto_ios/pubspec.yaml b/packages/native_crypto_ios/pubspec.yaml similarity index 95% rename from native_crypto_ios/pubspec.yaml rename to packages/native_crypto_ios/pubspec.yaml index 211e4de..f4de824 100644 --- a/native_crypto_ios/pubspec.yaml +++ b/packages/native_crypto_ios/pubspec.yaml @@ -1,6 +1,6 @@ name: native_crypto_ios description: iOS implementation of NativeCrypto -version: 0.0.7 +version: 0.1.1 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/native_crypto_platform_interface/.gitignore b/packages/native_crypto_platform_interface/.gitignore similarity index 100% rename from native_crypto_platform_interface/.gitignore rename to packages/native_crypto_platform_interface/.gitignore diff --git a/native_crypto_platform_interface/.metadata b/packages/native_crypto_platform_interface/.metadata similarity index 100% rename from native_crypto_platform_interface/.metadata rename to packages/native_crypto_platform_interface/.metadata diff --git a/packages/native_crypto_platform_interface/CHANGELOG.md b/packages/native_crypto_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..8b44888 --- /dev/null +++ b/packages/native_crypto_platform_interface/CHANGELOG.md @@ -0,0 +1,40 @@ +## 0.1.1 + + - **PERF**: x10 perfomance improvement on android with better list management. + - **FEAT**: export new exceptions. + +## 0.1.0 + +> Breaking changes ! + +* Follow **Federated Plugin** Flutter standard. + +## 0.0.6 + +* Add KeyPair generation. +* Rework exposed API. + +## 0.0.5 + +* New API. +* Add digest support. +* Clean platform specific code base. + +## 0.0.4 + +* Improve AES. + +## 0.0.3 + +* Add PBKDF2 support. +* Add exceptions. +* Improve documentation. + +## 0.0.2 + +* Add different key size support. +* Improve performances. + +## 0.0.1 + +* First AES cross-platform encryption & decryption implementation. diff --git a/packages/native_crypto_platform_interface/LICENSE b/packages/native_crypto_platform_interface/LICENSE new file mode 100644 index 0000000..68bb0c6 --- /dev/null +++ b/packages/native_crypto_platform_interface/LICENSE @@ -0,0 +1,23 @@ +NativeCrypto - Platform Interface + +MIT License + +Copyright (c) 2019 - 2022 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +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 diff --git a/packages/native_crypto_platform_interface/README.md b/packages/native_crypto_platform_interface/README.md new file mode 100644 index 0000000..d1cde17 --- /dev/null +++ b/packages/native_crypto_platform_interface/README.md @@ -0,0 +1,12 @@ +# NativeCrypto - Platform Interface + +A common platform interface for the [`native_crypto`][1] plugin. + +This interface allows platform-specific implementations of the `native_crypto` plugin, as well as the plugin itself, to ensure they are supporting the same interface. + +## Usage + +To implement a new platform-specific implementation of `native_crypto`, extend [`NativeCryptoPlatform`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `NativeCryptoPlatform` by calling `NativeCryptoPlatform.instance = MyNativeCryptoPlatform()`. + +[1]: ../native_crypto +[2]: lib/native_crypto_platform_interface.dart \ No newline at end of file diff --git a/packages/native_crypto_platform_interface/analysis_options.yaml b/packages/native_crypto_platform_interface/analysis_options.yaml new file mode 100644 index 0000000..db48808 --- /dev/null +++ b/packages/native_crypto_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml \ No newline at end of file diff --git a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart new file mode 100644 index 0000000..c85bda1 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart @@ -0,0 +1,14 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: native_crypto_platform_interface.dart +// Created Date: 24/05/2022 19:39:11 +// Last Modified: 24/05/2022 19:39:58 +// ----- +// Copyright (c) 2022 + +library native_crypto_platform_interface; + +export 'src/method_channel/method_channel_native_crypto.dart'; +export 'src/platform_interface/native_crypto_platform.dart'; +export 'src/utils/exception.dart'; diff --git a/packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart b/packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart new file mode 100644 index 0000000..0559ed4 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart @@ -0,0 +1,154 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: native_crypto_method_channel.dart +// Created Date: 25/12/2021 16:58:04 +// Last Modified: 25/05/2022 10:40:29 +// ----- +// Copyright (c) 2021 + +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +/// An implementation of [NativeCryptoPlatform] that uses method channels. +class MethodChannelNativeCrypto extends NativeCryptoPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + MethodChannel channel = const MethodChannel('plugins.hugop.cl/native_crypto'); + + @override + Future digest(Uint8List data, String algorithm) async { + try { + return await channel.invokeMethod( + 'digest', + { + 'data': data, + 'algorithm': algorithm, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future generateSecretKey(int bitsCount) async { + try { + return await channel.invokeMethod( + 'generateSecretKey', + { + 'bitsCount': bitsCount, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future pbkdf2( + String password, + String salt, + int keyBytesCount, + int iterations, + String algorithm, + ) async { + try { + return await channel.invokeMethod( + 'pbkdf2', + { + 'password': password, + 'salt': salt, + 'keyBytesCount': keyBytesCount, + 'iterations': iterations, + 'algorithm': algorithm, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future?> encryptAsList( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + try { + return await channel.invokeListMethod( + 'encryptAsList', + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future decryptAsList( + List data, + Uint8List key, + String algorithm, + ) async { + try { + return await channel.invokeMethod( + 'decryptAsList', + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future encrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + try { + return await channel.invokeMethod( + 'encrypt', + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future decrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + try { + return await channel.invokeMethod( + 'decrypt', + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } +} diff --git a/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart b/packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart similarity index 51% rename from native_crypto_platform_interface/lib/native_crypto_platform_interface.dart rename to packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart index 1d6fbd5..b09c0d3 100644 --- a/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart +++ b/packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart @@ -3,22 +3,23 @@ // ----- // File: native_crypto_platform_interface.dart // Created Date: 25/12/2021 16:43:49 -// Last Modified: 25/12/2021 17:39:39 +// Last Modified: 25/05/2022 22:11:02 // ----- // Copyright (c) 2021 import 'dart:typed_data'; -import './src/method_channel_native_crypto.dart'; -import './src/platform_interface.dart'; +import 'package:native_crypto_platform_interface/src/method_channel/method_channel_native_crypto.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; /// The interface that implementations of path_provider must implement. /// -/// Platform implementations should extend this class rather than implement it as `NativeCrypto` -/// does not consider newly added methods to be breaking changes. Extending this class -/// (using `extends`) ensures that the subclass will get the default implementation, while -/// platform implementations that `implements` this interface will be broken by newly added -/// [NativeCryptoPlatform] methods. +/// Platform implementations should extend this class rather than implement +/// it as `NativeCrypto` does not consider newly added methods to be +/// breaking changes. Extending this class (using `extends`) ensures +/// that the subclass will get the default implementation, while platform +/// implementations that `implements` this interface will be +/// broken by newly added [NativeCryptoPlatform] methods. abstract class NativeCryptoPlatform extends PlatformInterface { /// Constructs a NativeCryptoPlatform. NativeCryptoPlatform() : super(token: _token); @@ -29,13 +30,13 @@ abstract class NativeCryptoPlatform extends PlatformInterface { /// The default instance of [NativeCryptoPlatform] to use. /// - /// Defaults to [MethodChannelPathProvider]. + /// Defaults to [MethodChannelNativeCrypto]. static NativeCryptoPlatform get instance => _instance; /// Platform-specific plugins should set this with their own platform-specific /// class that extends [NativeCryptoPlatform] when they register themselves. static set instance(NativeCryptoPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } @@ -47,29 +48,45 @@ abstract class NativeCryptoPlatform extends PlatformInterface { throw UnimplementedError('generateSecretKey is not implemented'); } - Future generateKeyPair() { - throw UnimplementedError('generateKeyPair is not implemented'); + Future pbkdf2( + String password, + String salt, + int keyBytesCount, + int iterations, + String algorithm, + ) { + throw UnimplementedError('pbkdf2 is not implemented'); } - Future pbkdf2(String password, String salt, int keyBytesCount, - int iterations, String algorithm) { - throw UnimplementedError('pbkdf2 is not implemented'); + Future?> encryptAsList( + Uint8List data, + Uint8List key, + String algorithm, + ) { + throw UnimplementedError('encryptAsList is not implemented'); } - Future encrypt(Uint8List data, Uint8List key, String algorithm) { - throw UnimplementedError('encrypt is not implemented'); + Future decryptAsList( + List data, + Uint8List key, + String algorithm, + ) { + throw UnimplementedError('decryptAsList is not implemented'); } - Future decrypt(Uint8List data, Uint8List key, String algorithm) { - throw UnimplementedError('decrypt is not implemented'); + Future encrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) { + throw UnimplementedError('encrypt is not implemented'); } - Future generateSharedSecretKey( - Uint8List salt, - int keyBytesCount, - Uint8List ephemeralPrivateKey, - Uint8List otherPublicKey, - String hkdfAlgorithm) { - throw UnimplementedError('generateSharedSecretKey is not implemented'); + Future decrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) { + throw UnimplementedError('decrypt is not implemented'); } } diff --git a/packages/native_crypto_platform_interface/lib/src/utils/exception.dart b/packages/native_crypto_platform_interface/lib/src/utils/exception.dart new file mode 100644 index 0000000..b46bb4a --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/utils/exception.dart @@ -0,0 +1,126 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: exception.dart +// Created Date: 24/05/2022 18:54:48 +// Last Modified: 26/05/2022 20:36:04 +// ----- +// Copyright (c) 2022 + +// ignore_for_file: constant_identifier_names + +import 'dart:developer'; + +import 'package:flutter/services.dart'; + +enum NativeCryptoExceptionCode { + unknown, + not_implemented, + invalid_argument, + invalid_key, + invalid_key_length, + invalid_algorithm, + invalid_padding, + invalid_mode, + invalid_cipher, + invalid_data, + platform_not_supported, + platform_throws, + platform_returned_invalid_data, + platform_returned_empty_data, + platform_returned_null; + + String get code => toString().split('.').last.toLowerCase(); +} + +class NativeCryptoException implements Exception { + NativeCryptoException({ + this.message, + String? code, + this.stackTrace, + }) : code = code ?? NativeCryptoExceptionCode.unknown.code; + + /// The long form message of the exception. + final String? message; + + /// The optional code to accommodate the message. + final String code; + + /// The stack trace which provides information to the user about the call + /// sequence that triggered an exception + final StackTrace? stackTrace; + + @override + String toString() { + String output = '[NativeCryptoException/$code] $message'; + + if (stackTrace != null) { + output += '\n\n${stackTrace.toString()}'; + } + + return output; + } + + /// Catches a [PlatformException] and returns an [Exception]. + /// + /// If the [Exception] is a [PlatformException], + /// a [NativeCryptoException] is returned. + static Never convertPlatformException( + Object exception, + StackTrace stackTrace, + ) { + log(exception.toString()); + if (exception is! Exception || exception is! PlatformException) { + Error.throwWithStackTrace(exception, stackTrace); + } + + Error.throwWithStackTrace( + NativeCryptoException.fromPlatformException(exception, stackTrace), + stackTrace, + ); + } + + /// Converts a [PlatformException] into a [NativeCryptoException]. + /// + /// A [PlatformException] can only be converted to a [NativeCryptoException] + /// if the `details` of the exception exist. + factory NativeCryptoException.fromPlatformException( + PlatformException platformException, + StackTrace stackTrace, + ) { + final Map? details = platformException.details != null + ? Map.from( + platformException.details as Map, + ) + : null; + + String code = NativeCryptoExceptionCode.unknown.code; + String message = platformException.message ?? ''; + + if (details != null) { + code = details['code'] ?? code; + message = details['message'] ?? message; + } + + return NativeCryptoException( + message: message, + code: code, + stackTrace: stackTrace, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is NativeCryptoException && + other.message == message && + other.code == code && + other.stackTrace == stackTrace; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => message.hashCode ^ code.hashCode ^ stackTrace.hashCode; +} diff --git a/packages/native_crypto_platform_interface/pubspec.yaml b/packages/native_crypto_platform_interface/pubspec.yaml new file mode 100644 index 0000000..57593de --- /dev/null +++ b/packages/native_crypto_platform_interface/pubspec.yaml @@ -0,0 +1,25 @@ +name: native_crypto_platform_interface +description: A common interface for NativeCrypto plugin. +version: 0.1.1 + +environment: + sdk: ">=2.17.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + + plugin_platform_interface: ^2.1.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + + wyatt_analysis: + git: + url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages + ref: wyatt_analysis-v2.1.0 + path: packages/wyatt_analysis diff --git a/packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart b/packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart new file mode 100644 index 0000000..28c01a1 --- /dev/null +++ b/packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart @@ -0,0 +1,175 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: method_channel_native_crypto_test.dart +// Created Date: 25/05/2022 22:47:41 +// Last Modified: 25/05/2022 23:22:44 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto_platform_interface/src/method_channel/method_channel_native_crypto.dart'; + +void main() { + TestWidgetsFlutterBinding + .ensureInitialized(); // Required for setMockMethodCallHandler + + group('$MethodChannelNativeCrypto', () { + const MethodChannel channel = + MethodChannel('plugins.hugop.cl/native_crypto'); + final List log = []; + final MethodChannelNativeCrypto nativeCrypto = MethodChannelNativeCrypto(); + + TestDefaultBinaryMessengerBinding.instance?.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall call) async { + log.add(call); + return null; + }); + + // Run after each test. + tearDown(log.clear); + + test('digest', () async { + await nativeCrypto.digest(Uint8List(0), 'sha256'); + expect( + log, + [ + isMethodCall( + 'digest', + arguments: { + 'data': Uint8List(0), + 'algorithm': 'sha256', + }, + ), + ], + ); + }); + + test('generateSecretKey', () async { + await nativeCrypto.generateSecretKey(256); + expect( + log, + [ + isMethodCall( + 'generateSecretKey', + arguments: { + 'bitsCount': 256, + }, + ), + ], + ); + }); + + test('pbkdf2', () async { + await nativeCrypto.pbkdf2( + 'password', + 'salt', + 32, + 10000, + 'sha256', + ); + expect( + log, + [ + isMethodCall( + 'pbkdf2', + arguments: { + 'password': 'password', + 'salt': 'salt', + 'keyBytesCount': 32, + 'iterations': 10000, + 'algorithm': 'sha256', + }, + ), + ], + ); + }); + + test('encryptAsList', () async { + await nativeCrypto.encryptAsList( + Uint8List(0), + Uint8List(0), + 'aes', + ); + expect( + log, + [ + isMethodCall( + 'encryptAsList', + arguments: { + 'data': Uint8List(0), + 'key': Uint8List(0), + 'algorithm': 'aes', + }, + ), + ], + ); + }); + + test('decryptAsList', () async { + await nativeCrypto.decryptAsList( + [Uint8List(0)], + Uint8List(0), + 'aes', + ); + expect( + log, + [ + isMethodCall( + 'decryptAsList', + arguments: { + 'data': [Uint8List(0)], + 'key': Uint8List(0), + 'algorithm': 'aes', + }, + ), + ], + ); + }); + + test('encrypt', () async { + await nativeCrypto.encrypt( + Uint8List(0), + Uint8List(0), + 'aes', + ); + expect( + log, + [ + isMethodCall( + 'encrypt', + arguments: { + 'data': Uint8List(0), + 'key': Uint8List(0), + 'algorithm': 'aes', + }, + ), + ], + ); + }); + + test('decrypt', () async { + await nativeCrypto.decrypt( + Uint8List(0), + Uint8List(0), + 'aes', + ); + expect( + log, + [ + isMethodCall( + 'decrypt', + arguments: { + 'data': Uint8List(0), + 'key': Uint8List(0), + 'algorithm': 'aes', + }, + ), + ], + ); + }); + }); +} diff --git a/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart b/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart new file mode 100644 index 0000000..acd0f9d --- /dev/null +++ b/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart @@ -0,0 +1,166 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: native_crypto_platform_test.dart +// Created Date: 25/05/2022 21:43:25 +// Last Modified: 25/05/2022 23:26:18 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:native_crypto_platform_interface/src/platform_interface/native_crypto_platform.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +void main() { + late ExtendsNativeCryptoPlatform nativeCryptoPlatform; + + group('$NativeCryptoPlatform', () { + setUpAll(() { + nativeCryptoPlatform = ExtendsNativeCryptoPlatform(); + }); + test('Constructor', () { + expect(nativeCryptoPlatform, isA()); + expect(nativeCryptoPlatform, isA()); + }); + + test('get.instance', () { + expect( + NativeCryptoPlatform.instance, + isA(), + ); + }); + test('set.instance', () { + NativeCryptoPlatform.instance = ExtendsNativeCryptoPlatform(); + expect( + NativeCryptoPlatform.instance, + isA(), + ); + }); + + test('Cannot be implemented with `implements`', () { + expect( + () { + NativeCryptoPlatform.instance = ImplementsNativeCryptoPlatform(); + }, + throwsA(isInstanceOf()), + ); + }); + + test('Can be mocked with `implements`', () { + final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); + NativeCryptoPlatform.instance = mock; + }); + + test('Can be extended', () { + NativeCryptoPlatform.instance = ExtendsNativeCryptoPlatform(); + }); + + test('throws if .digest() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform.digest(Uint8List(0), 'sha256'), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'digest is not implemented', + ), + ), + ); + }); + + test('throws if .generateSecretKey() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform.generateSecretKey(256), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'generateSecretKey is not implemented', + ), + ), + ); + }); + + test('throws if .pbkdf2() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform.pbkdf2('password', 'salt', 0, 0, 'sha256'), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'pbkdf2 is not implemented', + ), + ), + ); + }); + + test('throws if .encryptAsList() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform.encryptAsList( + Uint8List(0), + Uint8List(0), + 'aes', + ), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'encryptAsList is not implemented', + ), + ), + ); + }); + + test('throws if .decryptAsList() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform + .decryptAsList([Uint8List(0)], Uint8List(0), 'aes'), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'decryptAsList is not implemented', + ), + ), + ); + }); + + test('throws if .encrypt() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform.encrypt(Uint8List(0), Uint8List(0), 'aes'), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'encrypt is not implemented', + ), + ), + ); + }); + + test('throws if .decrypt() not implemented', () async { + await expectLater( + () => nativeCryptoPlatform.decrypt(Uint8List(0), Uint8List(0), 'aes'), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'decrypt is not implemented', + ), + ), + ); + }); + }); +} + +class ExtendsNativeCryptoPlatform extends NativeCryptoPlatform {} + +class ImplementsNativeCryptoPlatform extends Mock + implements NativeCryptoPlatform {} + +class MockNativeCryptoPlatform extends Mock + with MockPlatformInterfaceMixin + implements NativeCryptoPlatform {} diff --git a/resources/bench_nc_android_oneplus_A5010.csv b/resources/bench_nc_android_oneplus_A5010.csv new file mode 100644 index 0000000..5789c98 --- /dev/null +++ b/resources/bench_nc_android_oneplus_A5010.csv @@ -0,0 +1,14 @@ +Run;Size (B);Encryption Time (ms);Decryption Time (ms) +1;2097152;48;64 +2;6291456;52;52 +3;10485760;105;83 +4;14680064;96;198 +5;18874368;116;143 +6;23068672;253;257 +7;27262976;164;213 +8;31457280;271;270 +9;35651584;339;356 +10;39845888;316;320 +11;44040192;370;412 +12;48234496;425;368 +13;52428800;430;402 \ No newline at end of file diff --git a/resources/bench_nc_ios_iphone13.csv b/resources/bench_nc_ios_iphone13.csv new file mode 100644 index 0000000..47ef2ec --- /dev/null +++ b/resources/bench_nc_ios_iphone13.csv @@ -0,0 +1,14 @@ +Run;Size (B);Encryption Time (ms);Decryption Time (ms) +1;2097152;20;16 +2;6291456;13;13 +3;10485760;16;22 +4;14680064;23;37 +5;18874368;21;48 +6;23068672;34;45 +7;27262976;38;72 +8;31457280;59;72 +9;35651584;70;74 +10;39845888;61;85 +11;44040192;80;92 +12;48234496;90;113 +13;52428800;79;111 \ No newline at end of file diff --git a/resources/bench_pc_android_oneplus_A5010.csv b/resources/bench_pc_android_oneplus_A5010.csv new file mode 100644 index 0000000..48346c9 --- /dev/null +++ b/resources/bench_pc_android_oneplus_A5010.csv @@ -0,0 +1,9 @@ +size;encryption time;encode time;decryption time;crypto time +2000000;7764;7302;0 +4000000;14720;14580;0 +8000000;29421;29037;0 +16000000;58874;58304;0 +32000000;117964;116570;0 +64000000;235122;232923;0 +128000000;469697;466278;0 +256000000;0;0;0 \ No newline at end of file diff --git a/resources/bench_pc_ios_iphone13.csv b/resources/bench_pc_ios_iphone13.csv new file mode 100644 index 0000000..3f01464 --- /dev/null +++ b/resources/bench_pc_ios_iphone13.csv @@ -0,0 +1,14 @@ +Run;Size (B);Encryption Time (ms);Decryption Time (ms) +1;2097152;2420;2289 +2;6291456;7118;7369 +3;10485760;12766;13051 +4;14680064;18594;19029 +5;18874368;27449;27927 +6;23068672;34093;33863 +7;27262976;39855;39981 +8;31457280;46359;46089 +9;35651584;52596;52375 +10;39845888;59151;58881 +11;44040192;65914;65590 +12;48234496;71908;72099 +13;52428800;79202;78571 \ No newline at end of file diff --git a/resources/benchmarks.png b/resources/benchmarks.png new file mode 100644 index 0000000..62dac68 Binary files /dev/null and b/resources/benchmarks.png differ diff --git a/resources/native_crypto.png b/resources/native_crypto.png new file mode 100644 index 0000000..e3fdadf Binary files /dev/null and b/resources/native_crypto.png differ