Compare commits

...

68 Commits

Author SHA1 Message Date
cab2843706
chore(release): publish packages
All checks were successful
continuous-integration/drone/push Build is passing
- native_crypto@0.2.0
 - native_crypto_example@0.0.1
2023-04-05 17:11:43 +02:00
3cab93ff71
chore(release): publish packages
All checks were successful
continuous-integration/drone/push Build is passing
- native_crypto_android@0.1.2
 - native_crypto_ios@0.1.2
 - native_crypto_platform_interface@0.2.0
2023-04-05 17:07:07 +02:00
c98b9947b4
docs: update readmes/licences
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-05 17:02:38 +02:00
01832a3b03
chore(api): file format + update readme file 2023-04-05 16:46:35 +02:00
7dc07c693a
feat(api): update example for ios file encryption 2023-04-05 16:42:16 +02:00
550fe8b73e
refactor(ios): remove useless lines/classes 2023-04-05 16:41:54 +02:00
7c8f7206f0
feat(api): update example with benchmark + file encryption 2023-04-05 15:17:56 +02:00
0bf72447a0
fix(android): file encryption 2023-04-05 15:16:45 +02:00
108c394a25
test(api): update mocks with new interface 2023-04-04 23:24:20 +02:00
e47004e2d0
feat(api): update example 2023-04-04 23:23:47 +02:00
2f22cc549d
fix(api): accept empty decrypted plaintext 2023-04-04 23:22:43 +02:00
5be6296829
fix(ios): key length in bits 2023-04-04 23:21:39 +02:00
68217ac4b9
feat(ios): use swift pigeon generator 2023-04-04 22:37:12 +02:00
560f5b4942
feat(android): use kotlin pigeon generator 2023-04-04 22:36:50 +02:00
f570ed076a
feat(interface)!: set pigeon as default implementation 2023-04-04 22:36:10 +02:00
c8ff1149d7
feat(api)!: rework full api with better object oriented architecture 2023-02-22 20:16:40 +01:00
8044ccfa43
feat(interface): make api injectable for test 2023-02-22 20:13:51 +01:00
d8cf8dddc4
feat(android): generate pigeon messages 2023-02-22 17:31:58 +01:00
ccb51adbc4
feat(ios): generate pigeon messages 2023-02-22 17:31:12 +01:00
0a040d2971
feat(interface)!: add pigeon + add hmac + remove useless decryption method 2023-02-22 17:27:58 +01:00
ff981b2361
docs: add android/ios development instructions 2023-02-22 17:25:58 +01:00
f47c352efb
docs: add uml models 2023-02-22 17:25:20 +01:00
38cb0a5988
build: add vscode settings 2023-02-22 17:24:46 +01:00
39badb5613
chore(interface): update deps
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-07 17:07:36 +01:00
3b03e05efe
ci: update drone, melos and pre-commit config 2023-01-07 17:05:06 +01:00
8f4041d7bf Merge pull request 'chore(deps): update dependency flutter_lints to v2' (#5) from renovate/flutter_lints-2.x into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #5
2022-06-01 14:21:03 +00:00
Renovate
897b97f5d5 chore(deps): update dependency flutter_lints to v2
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-01 14:11:26 +00:00
bee0b3e38e Merge pull request 'Fix/Update' (#1) from Fix/Update into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #1
2022-06-01 11:51:15 +00:00
4fde7d0aa2
ci: add badge
All checks were successful
continuous-integration/drone/pr Build is passing
2022-06-01 13:47:38 +02:00
d28a49a67b
docs: update readme
All checks were successful
continuous-integration/drone Build is passing
2022-06-01 12:21:49 +02:00
cf4227fb58
ci: add drone config file 2022-05-27 18:18:58 +02:00
ac35cd89ca
style: format all + melos test command 2022-05-27 17:34:53 +02:00
1b00d20ec5
fix: update code to pass tests 2022-05-27 16:41:43 +02:00
cefa73ec3d
test: add cipher tests 2022-05-27 16:41:25 +02:00
ebdcf00c15
fix: update code to pass all tests 2022-05-26 23:23:13 +02:00
96f9aad1b3
test: (WIP) add some tests 2022-05-26 23:22:14 +02:00
c5d42feef4
feat(api): add exception code for platform throw 2022-05-26 20:43:43 +02:00
48ebabb54c
feat: rework bytearray and memory optimization, simplify API 2022-05-26 20:42:53 +02:00
6939a8df7e
refactor: (WIP) optimize exceptions and bytearray 2022-05-26 16:26:16 +02:00
9bfe969c7d
test: (WIP) add mocks and tests for secret key 2022-05-26 16:25:35 +02:00
81335dc350
test(platform): add tests for platform and method channel 2022-05-25 23:31:01 +02:00
9aa4eeb567
fix: update verify function 2022-05-25 23:30:13 +02:00
e016d640c4
doc: copy readme 2022-05-25 23:29:13 +02:00
39a0a44730
fix: change tag length in aes gcm cipher 2022-05-25 23:29:02 +02:00
5729fff09b
refactor(example): update benchmark page 2022-05-25 23:27:22 +02:00
f592799970
docs: update readme 2022-05-25 21:33:51 +02:00
ff6af2491a
chore(release): publish packages
- native_crypto@0.1.1
2022-05-25 16:26:10 +02:00
ee79b8d20f
chore(release): publish packages
- native_crypto_ios@0.1.1
 - native_crypto_android@0.1.1
 - native_crypto_platform_interface@0.1.1
2022-05-25 16:23:41 +02:00
0bb6aa4b78
fix: benchmark output 2022-05-25 15:43:51 +02:00
2ed8aab69f
perf(ios): optimize swift code 2022-05-25 15:43:12 +02:00
142dd17ad2
refactor(ios): rework swift part 2022-05-25 15:42:56 +02:00
a1112b5c80
feat: export new exceptions 2022-05-25 10:51:20 +02:00
a7affea1e1
perf: x10 perfomance improvement on android with better list management 2022-05-24 23:59:10 +02:00
6397e10c05
feat(example): add PointyCastle benchmark 2022-05-24 18:43:46 +02:00
41354e3dc4
refactor(android): clean and modernize kotlin code 2022-05-24 18:43:14 +02:00
2fe4172131
docs: add link to readme file 2022-05-23 23:15:00 +02:00
70a6fda3ed
refactor: change file organization 2022-05-23 23:11:04 +02:00
32106f549f
fix: update and fix code 2022-05-23 21:54:48 +02:00
51f6e6aa24
style(platform): reformat code 2022-05-23 21:54:16 +02:00
2559112beb
docs: add some resources 2022-05-23 21:53:08 +02:00
7c1208156e
chore: update entire repo 2022-05-23 21:24:27 +02:00
7fc1ef5968 Add all 2022-01-14 19:12:42 +01:00
8cd192c6b0 Add native examples 2021-12-28 18:20:06 +01:00
41b59b2b93 Investigate in slow data transferts 2021-12-28 17:10:18 +01:00
3381ff67b9 Add new public api and example 2021-12-28 16:03:50 +01:00
c01e0a12ba Add android implementation 2021-12-28 16:03:07 +01:00
9b66f2044a Update license, and readme 2021-12-28 16:02:55 +01:00
9e0d921564 Switch to Federated Plugin layout 2021-12-27 21:13:26 +01:00
320 changed files with 13311 additions and 3486 deletions

20
.drone.yml Normal file
View File

@ -0,0 +1,20 @@
kind: pipeline
type: docker
name: default
steps:
- name: quality-check
image: git.wyatt-studio.fr/wyatt-foss/flutter-melos:2.9.0
commands:
- melos run quality-check
- melos run publish:validate
- name: publish
image: git.wyatt-studio.fr/wyatt-foss/flutter-melos:2.9.0
commands:
- melos run publish:validate
trigger:
branch:
- master
event:
- push

468
.gitignore vendored
View File

@ -1,107 +1,7 @@
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
# Created by https://www.gitignore.io/api/windows,visualstudiocode,android,androidstudio,cocoapods,dart,flutter,intellij+all,kotlin,macos,swift,xcode,xcodeinjection
# Edit at https://www.gitignore.io/?templates=windows,visualstudiocode,android,androidstudio,cocoapods,dart,flutter,intellij+all,kotlin,macos,swift,xcode,xcodeinjection
### Android ###
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
### Android Patch ###
gen-external-apklibs
output.json
# Replacement of .externalNativeBuild directories introduced
# with Android Studio 3.5.
.cxx/
### CocoaPods ###
## CocoaPods GitIgnore Template
# CocoaPods - Only use to conserve bandwidth / Save time on Pushing
# - Also handy if you have a large number of dependant pods
# - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
Pods/
# 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
@ -109,6 +9,7 @@ Pods/
# Files and directories created by pub
.dart_tool/
.packages
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
@ -116,6 +17,9 @@ pubspec.lock
# If you don't generate documentation locally you can remove this line.
doc/api/
# dotenv environment variables file
.env*
# Avoid committing generated Javascript files:
*.dart.js
*.info.json # Produced by the --dump-info flag.
@ -125,13 +29,25 @@ doc/api/
*.js.deps
*.js.map
.flutter-plugins
.flutter-plugins-dependencies
### Dart Patch ###
# dotenv environment variables file
.env
### Flutter ###
# Flutter/Dart/Pub related
**/doc/api/
.flutter-plugins
.flutter-plugins-dependencies
.fvm/
.pub-cache/
.pub/
coverage/
lib/generated_plugin_registrant.dart
# For library packages, dont commit the pubspec.lock file.
# Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies.
# See https://dart.dev/guides/libraries/private-files#pubspeclock
#pubspec.lock
# Android related
**/android/**/gradle-wrapper.jar
@ -139,6 +55,7 @@ doc/api/
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/key.properties
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
@ -159,12 +76,15 @@ doc/api/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
@ -175,8 +95,34 @@ doc/api/
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
### 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
# User-specific stuff
@ -186,6 +132,9 @@ doc/api/
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
@ -206,6 +155,9 @@ doc/api/
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
@ -222,6 +174,7 @@ cmake-build-*/
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
@ -232,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
@ -244,20 +200,14 @@ 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
### JetBrains+all Patch ###
# Ignore everything but code style settings and run configurations
# that are supposed to be shared within teams.
.idea/
.idea/*
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
!.idea/codeStyles
!.idea/runConfigurations
### Kotlin ###
# Compiled class file
@ -265,22 +215,27 @@ modules.xml
# Log file
# 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 ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
@ -291,6 +246,7 @@ hs_err_pid*
# Icon must end with two \r
Icon
# Thumbnails
._*
@ -310,15 +266,25 @@ 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
## Build generated
DerivedData/
## User settings
xcuserdata/
## Various settings
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
@ -327,32 +293,15 @@ DerivedData/
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
.build/
# Add this line if you want to avoid checking in Xcode SPM integration.
# .swiftpm/xcode
# CocoaPods
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
@ -365,19 +314,18 @@ 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/
.accio/
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.
# 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
@ -385,17 +333,102 @@ fastlane/screenshots/**/*.png
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
# Swift Package Manager
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# 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
# Accio dependency management
Dependencies/
.accio/
# 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
# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
### VisualStudioCode ###
.vscode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.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
# Ignore code-workspaces
*.code-workspace
### Windows ###
# Windows thumbnail cache files
@ -424,121 +457,26 @@ $RECYCLE.BIN/
*.lnk
### Xcode ###
# Xcode
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
## Xcode 8 and earlier
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
## Xcode Patch
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
### Xcode Patch ###
**/xcshareddata/WorkspaceSettings.xcsettings
### XcodeInjection ###
# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
# Built application files
# Files for the ART/Dalvik VM
# Java class files
# Generated files
# Gradle files
.gradle
# Signing files
.signing/
# Local configuration file (sdk path, etc)
# Proguard folder generated by Eclipse
# Log Files
# Android Studio
/*/build/
/*/local.properties
/*/out
/*/*/build
/*/*/production
*~
*.swp
# Android Patch
# External native build folder generated in Android Studio 2.2 and later
# NDK
obj/
# IntelliJ IDEA
/out/
# User-specific configurations
.idea/caches/
.idea/libraries/
.idea/shelf/
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/scopes/scope_settings.xml
.idea/vcs.xml
.idea/jsLibraryMappings.xml
.idea/datasources.xml
.idea/dataSources.ids
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# OS-specific files
.DS_Store?
# Legacy Eclipse project files
.classpath
.project
.cproject
.settings/
# Mobile Tools for Java (J2ME)
# Package Files #
# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
## Plugin-specific files:
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Mongo Explorer plugin
.idea/mongoSettings.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
# End of https://www.gitignore.io/api/windows,visualstudiocode,android,androidstudio,cocoapods,dart,flutter,intellij+all,kotlin,macos,swift,xcode,xcodeinjection
# 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

7
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,7 @@
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v2.1.1
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: [build, ci, docs, feat, fix, perf, refactor, style, test, chore]

28
.vscode/launch.json vendored Normal file
View File

@ -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"
},
]
}

19
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"bloc.newCubitTemplate.type": "equatable",
"psi-header.config": {
"blankLinesAfter": 0,
"forceToTop": true,
},
"psi-header.templates": [
{
"language": "*",
"template": [
"Copyright 2019-<<year>> <<author>>",
"",
"Use of this source code is governed by an MIT-style",
"license that can be found in the LICENSE file or at",
"https://opensource.org/licenses/MIT.",
]
}
],
}

6
AUTHORS Normal file
View File

@ -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 <email address>
Hugo Pointcheval <git@pcl.ovh>

View File

@ -1,25 +1,157 @@
## 0.0.6
# Change Log
**WIP...**
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 0.0.5
## 2023-04-05
* New API
* Add digest support
* Clean platform specific code base
## 0.0.4
### Changes
* Improve AES
---
## 0.0.3
Packages with breaking changes:
* Add PBKDF2 support
* Add exceptions
* Improve documentation
## 0.0.2
- [`native_crypto` - `v0.2.0`](#native_crypto---v020)
* Add different key size support
* Improve performances
## 0.0.1
Packages with other changes:
- [`native_crypto_example` - `v0.0.1`](#native_crypto_example---v001)
---
#### `native_crypto` - `v0.2.0`
- **REFACTOR**: (WIP) optimize exceptions and bytearray.
- **REFACTOR**: update benchmark page.
- **FIX**: accept empty decrypted plaintext.
- **FIX**: update code to pass tests.
- **FIX**: update code to pass all tests.
- **FIX**: change tag length in aes gcm cipher.
- **FEAT**: update example for ios file encryption.
- **FEAT**: update example with benchmark + file encryption.
- **FEAT**: update example.
- **FEAT**: rework bytearray and memory optimization, simplify API.
- **DOCS**: update readmes/licences.
- **DOCS**: update readme.
- **BREAKING** **FEAT**: rework full api with better object oriented architecture.
#### `native_crypto_example` - `v0.0.1`
- **REFACTOR**: update benchmark page.
- **REFACTOR**: change file organization.
- **PERF**: x10 perfomance improvement on android with better list management.
- **FIX**: update code to pass all tests.
- **FIX**: benchmark output.
- **FIX**: update and fix code.
- **FEAT**: update example for ios file encryption.
- **FEAT**: update example with benchmark + file encryption.
- **FEAT**: update example.
- **FEAT**: rework bytearray and memory optimization, simplify API.
- **FEAT**: export new exceptions.
- **FEAT**: add PointyCastle benchmark.
## 2023-04-05
### Changes
---
Packages with breaking changes:
- [`native_crypto_platform_interface` - `v0.2.0`](#native_crypto_platform_interface---v020)
Packages with other changes:
- [`native_crypto_android` - `v0.1.2`](#native_crypto_android---v012)
- [`native_crypto_ios` - `v0.1.2`](#native_crypto_ios---v012)
---
#### `native_crypto_platform_interface` - `v0.2.0`
- **REFACTOR**: (WIP) optimize exceptions and bytearray.
- **FIX**: update verify function.
- **FEAT**: make api injectable for test.
- **FEAT**: add exception code for platform throw.
- **DOCS**: update readmes/licences.
- **BREAKING** **FEAT**: set pigeon as default implementation.
- **BREAKING** **FEAT**: add pigeon + add hmac + remove useless decryption method.
#### `native_crypto_android` - `v0.1.2`
- **FIX**: file encryption.
- **FEAT**: use kotlin pigeon generator.
- **FEAT**: generate pigeon messages.
- **DOCS**: update readmes/licences.
#### `native_crypto_ios` - `v0.1.2`
- **REFACTOR**: remove useless lines/classes.
- **FIX**: key length in bits.
- **FEAT**: use swift pigeon generator.
- **FEAT**: generate pigeon messages.
- **DOCS**: update readmes/licences.
## 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.
* First AES cross-platform encryption & decryption implementation.

View File

@ -1,8 +1,6 @@
native_crypto
MIT License
Copyright (c) 2021 Hugo Pointcheval
Copyright (c) 2020-2023 Hugo Pointcheval
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

331
README.md
View File

@ -1,167 +1,280 @@
# NativeCrypto for Flutter
<p align="center">
<img width="700px" src="resources/native_crypto.png" style="background-color: rgb(255, 255, 255)">
<h5 align="center">Fast and powerful cryptographic functions for Flutter.</h5>
</p>
<p align="center">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
</a>
<a href="https://github.com/invertase/melos">
<img src="https://img.shields.io/badge/Maintained%20with-melos-f700ff.svg?style=flat-square" alt="Maintained with Melos" />
</a>
<a href="https://drone.wyatt-studio.fr/hugo/native-crypto">
<img src="https://drone.wyatt-studio.fr/api/badges/hugo/native-crypto/status.svg" alt="Build Status" />
</a>
</p>
![NativeCrypto Logo](/assets/native_crypto.png)
---
Fast and powerful cryptographic functions thanks to **javax.crypto** and **CommonCrypto**.
[[Changelog]](./CHANGELOG.md) | [[License]](./LICENSE)
## 📝 Table of Contents
---
- [About](#about)
- [Getting Started](#getting_started)
- [Example](#example)
- [Usage](#usage)
- [Built Using](#built_using)
- [TODOS](#todos)
- [Authors](#authors)
## About
## 🧐 About <a name = "about"></a>
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/)
The goal of this plugin is to provide simple access to fast and powerful cryptographic functions by calling native libraries. So on **Android** the plugin uses *javax.crypto* and on **iOS** it uses *CommonCrypto*.
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:
I started this project because using **Pointy Castle** I faced big performance issues on smartphone. It's quite simple, an encryption of 1MB of data in AES256 on an Android device takes **20s** with Pointy Castle against **27ms** using NativeCrypto.
![](resources/benchmarks.png)
![Pointy Castle Benchmark](/assets/benchmark_pointycastle.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.
> We also notice on this benchmark that the AES encryption time does not even increase linearly with size.
In short, NativeCrypto is incomparable with PointyCastle.
As for NativeCrypto, here is a benchmark realized on an iPhone 11.
## Features
| Size (kB) | NativeCrypto **encryption** time (ms) |
|-----------|---------------------------------------|
| 2 mB | 13 ms
| 4 mB | 17 ms
| 8 mB | 56 ms
| 16 mB | 73 ms
| 32 mB | 120 ms
| 64 mB | 243 ms
| 128 mB | 509 ms
| 256 mB | 1057 ms
* Hash functions
- SHA-256
- SHA-384
- SHA-512
* HMAC functions
- HMAC-SHA-256
- HMAC-SHA-384
- HMAC-SHA-512
* Secure random
* PBKDF2
* AES
- Uint8List encryption/decryption
- File encryption/decryption
> ~1s for 256 mB !
In short, **NativeCrypto** is incomparable to **Pointy Castle** in terms of performance.
## 🏁 Getting Started <a name = "getting_started"></a>
### Prerequisites
You'll need:
- Flutter
### Installing
Add these lines in your **pubspec.yaml**:
```yaml
native_crypto:
git:
url: https://github.com/hugo-pcl/native-crypto-flutter.git
ref: v0.0.x
```
> Replace "x" with the current version!
Then in your code:
## Quick start
```dart
import 'package:native_crypto/native_crypto.dart';
Future<void> main() async {
// Message to encrypt
final Uint8List message = 'Hello World!'.toBytes();
// Ask user for a password
final String password = await getPassword();
// Initialize a PBKDF2 object
final Pbkdf2 pbkdf2 = Pbkdf2(
length: 32, // 32 bytes
iterations: 1000,
salt: 'salt'.toBytes(),
hashAlgorithm: HashAlgorithm.sha256,
);
// Derive a secret key from the password
final SecretKey secretKey = await pbkdf2(password: password);
// Initialize an AES cipher
final AES cipher = AES(
key: secretKey,
mode: AESMode.gcm,
padding: AESPadding.none,
);
// Encrypt the message
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);
// Decrypt the message
final Uint8List decryptedMessage = await cipher.decrypt(cipherText);
// Verify and print the decrypted message
assert(listEquals(message, decryptedMessage));
print(decryptedMessage.toStr());
}
```
## 🔍 Example <a name="example"></a>
Check the [example](./native_crypto/example) for a complete example.
Look in **example/lib/** for an example app.
Please take a look a the compatibility table below to check if your target is supported.
## 🎈 Usage <a name="usage"></a>
> Note: This **Flutter** example must run on a real device or a simulator.
To derive a key with **PBKDF2**.
## Usage
```dart
PBKDF2 _pbkdf2 = PBKDF2(keyLength: 32, iteration: 1000, hash: HashAlgorithm.SHA512);
await _pbkdf2.derive(password: "password123", salt: 'salty');
SecretKey key = _pbkdf2.key;
#### Compatibility
First, check compatibility with your targets.
| iOS | Android | MacOS | Linux | Windows | Web |
| --- | ------- | ----- | ----- | ------- | --- |
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md))
NativeCrypto ciphertexts are formatted as follow:
```
+------------------+--------------------+------------------+
| Nonce (12 bytes) | Cipher text (n-28) | Tag (16 bytes) |
+------------------+--------------------+------------------+
```
To generate a key, and create an **AES Cipher** instance.
> Warning: If your data comes from another source, make sur to use the same format.
#### Hash
To digest a message, you'll need to initialize a Hasher object implementing `Hash` . Then, you can digest your message.
```dart
AESCipher aes = await AESCipher.generate(
AESKeySize.bits256,
CipherParameters(
BlockCipherMode.CBC,
PlainTextPadding.PKCS5,
),
Hash hasher = Sha256();
Uint8List digest = await hasher.digest(message);
```
> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512
#### HMAC
To generate a HMAC, you'll need to initialize a `Hmac` object. Then, you can generate a HMAC from a message and a secret key.
```dart
Hmac hmac = HmacSha256();
Uint8List hmac = await hmac.digest(message, secretKey);
```
> In NativeCrypto, you can use the following HMAC functions: HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512
#### Keys
You can build a `SecretKey` from utf8, utf16, base64, base16 (hex) strings, int list or raw bytes. You can also generate a SecretKey from secure random.
```dart
SecretKey secretKey = SecretKey(bytes); // bytes is a Uint8List
SecretKey secretKey = SecretKey.fromUtf8('secret');
SecretKet secretKey = SecretKey.fromUtf16('secret');
SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0');
SecretKey secretKey = SecretKey.fromBase16('63657274');
SecretKey secretKey = SecretKey.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]);
SecretKey secretKey = await SecretKey.fromSecureRandom(32); // 32 bytes
```
#### Key derivation
You can derive a `SecretKey` using **PBKDF2**.
First, you need to initialize a `Pbkdf2` object.
```dart
final Pbkdf2 pbkdf2 = Pbkdf2(
length: 32, // 32 bytes
iterations: 1000,
salt: salt.toBytes(),
hashAlgorithm: HashAlgorithm.sha256,
);
```
You can also generate key, then create **AES Cipher**.
Then, you can derive a `SecretKey` from a password.
```dart
SecretKey _key = await SecretKey.generate(256, CipherAlgorithm.AES);
AESCipher aes = AESCipher(
_key,
CipherParameters(
BlockCipherMode.CBC,
PlainTextPadding.PKCS5,
),
SecretKey secretKey = await pbkdf2(password: password);
```
> Note: Pbkdf2 is a callable class. You can use it like a function.
#### Cipher
And now, you can use the `SecretKey` to encrypt/decrypt a message.
First, you need to initialize a `Cipher` object.
```dart
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
```
Then you can encrypt/decrypt data with this cipher.
Then, you can encrypt your message.
```dart
CipherText cipherText = await aes.encrypt(data);
Uint8List plainText = await aes.decrypt(cipherText);
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);
```
You can easely get encrypted bytes and IV from a CipherText
After an encryption you obtain a `CipherText` which contains chunks. You can get the underlying bytes with `cipherText.bytes` .
Uppon receiving encrypted message `receivedData` , you can decrypt it.
You have to reconstruct the ciphertext and the setup the chunk factory.
```dart
Uint8List bytes = cipherText.bytes;
Uint8List iv = cipherText.iv;
final CipherText<AESCipherChunk> receivedCipherText CipherText(
receivedData,
chunkFactory: (bytes) => AESCipherChunk(
bytes,
ivLength: cipher.mode.ivLength,
tagLength: cipher.mode.tagLength,
),
),
```
To create a cipher text with custom data.
Then, you can decrypt your message.
```dart
CipherText cipherText = AESCipherText(bytes, iv);
Uint8List message = await cipher.decrypt(receivedCipherText);
```
To create a hashed message
#### Files
You can encrypt/decrypt files.
First, you need to initialize a `Cipher` object.
```dart
MessageDigest md = MessageDigest.getInstance("sha256");
Uint8List hash = await md.digest(message);
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
```
## ⛏️ Built Using <a name = "built_using"></a>
Then, you can encrypt your file.
- [Dart](https://dart.dev)
- [Flutter](https://flutter.dev) - Framework
- [Kotlin](https://kotlinlang.org) - Android Specific code
- [Swift](https://www.apple.com/fr/swift/) - iOS Specific code
```dart
await cipher.encryptFile(plainText, cipherText);
```
## 🚀 TODOS <a name = "todos">
> Note: `plainText` and `cipherText` are `File` objects.
Here you can check major changes, roadmap and todos.
You can decrypt your file.
I plan to deal with asymmetric cryptography with the implementation of a Key Encapsulation Mechanism.
```dart
await cipher.decryptFile(cipherText, plainText);
```
- [x] Add PBKDF2 support.
- [x] Implement working cross platform AES encryption/decryption.
- [x] Add Different key sizes support.
- [x] Add exceptions.
- [x] Clean platform specific code.
- [x] Add digest.
- [x] Rework exposed API.
- [x] Add KeyPair generation.
- [ ] Add KEM.
- [ ] Porting NativeCrypto to other platforms...
#### Advanced
You can contribute to this project.
You can force the use of a specific IV. Please note that the IV must be unique for each encryption.
## ✍️ Authors <a name = "authors"></a>
```dart
final CipherText<AESCipherChunk> cipherText = await cipher.encryptWithIV(message, iv);
```
- [Hugo Pointcheval](https://github.com/hugo-pcl) - Idea & Initial work
- [Chisom Maxwell](https://github.com/maxcotech) - For the chunks idea [#2](https://github.com/hugo-pcl/native-crypto-flutter/issues/2)
⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing.
## Development
### Android
> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2b-add-android-platform-code-ktjava
* Launch Android Studio.
* Select Open an existing Android Studio Project in the Welcome to Android Studio dialog, or select File > Open from the menu, and select the `packages/native_crypto/example/android/build.gradle` file.
* In the Gradle Sync dialog, select OK.
* In the Android Gradle Plugin Update dialog, select Dont remind me again for this project.
### iOS
> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2c-add-ios-platform-code-swifthm
* Launch Xcode.
* Select File > Open, and select the `packages/native_crypto/example/ios/Runner.xcworkspace` file.

View File

@ -1,4 +0,0 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View File

@ -1 +0,0 @@
rootProject.name = 'native_crypto'

View File

@ -1,93 +0,0 @@
package fr.pointcheval.native_crypto
import java.lang.Exception
import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
enum class CipherAlgorithm(val spec: String) {
AES("AES"),
}
enum class BlockCipherMode(val instance: String) {
CBC("CBC"),
GCM("GCM"),
}
enum class Padding(val instance: String) {
PKCS5("PKCS5Padding"),
None("NoPadding")
}
class CipherParameters(private val mode: BlockCipherMode, private val padding: Padding) {
override fun toString(): String {
return mode.instance + "/" + padding.instance
}
}
class Cipher {
fun getCipherAlgorithm(dartAlgorithm: String) : CipherAlgorithm {
return when (dartAlgorithm) {
"aes" -> CipherAlgorithm.AES
else -> CipherAlgorithm.AES
}
}
fun getInstance(mode : String, padding : String) : CipherParameters {
val m = when (mode) {
"cbc" -> BlockCipherMode.CBC
"gcm" -> BlockCipherMode.GCM
else -> throw Exception()
}
val p = when (padding) {
"pkcs5" -> Padding.PKCS5
else -> Padding.None
}
return CipherParameters(m,p)
}
fun encrypt(data: ByteArray, key: ByteArray, algorithm: String, mode: String, padding: String) : List<ByteArray> {
val algo = getCipherAlgorithm(algorithm)
val params = getInstance(mode, padding)
val keySpecification = algo.spec + "/" + params.toString()
val mac = Hash().digest(key + data)
val sk: SecretKey = SecretKeySpec(key, algo.spec)
val cipher = Cipher.getInstance(keySpecification)
cipher.init(Cipher.ENCRYPT_MODE, sk)
val encryptedBytes = cipher.doFinal(mac + data)
val iv = cipher.iv
return listOf(encryptedBytes, iv);
}
fun decrypt(payload: Collection<ByteArray>, key: ByteArray, algorithm: String, mode: String, padding: String) : ByteArray? {
val algo = getCipherAlgorithm(algorithm)
val params = getInstance(mode, padding)
val keySpecification = algo.spec + "/" + params.toString()
val sk: SecretKey = SecretKeySpec(key, algo.spec)
val cipher = Cipher.getInstance(keySpecification);
val iv = payload.last();
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.DECRYPT_MODE, sk, ivSpec);
val decryptedBytes = cipher.doFinal(payload.first());
val mac = decryptedBytes.copyOfRange(0, 32)
val decryptedContent : ByteArray = decryptedBytes.copyOfRange(32, decryptedBytes.size)
val verificationMac = Hash().digest(key + decryptedContent)
return if (mac.contentEquals(verificationMac)) {
decryptedContent
} else {
null;
}
}
}

View File

@ -1,42 +0,0 @@
package fr.pointcheval.native_crypto
import java.security.MessageDigest
enum class HashAlgorithm(val length : Int) {
SHA1(160),
SHA224(224),
SHA256(256),
SHA384(384),
SHA512(512);
}
class Hash() {
fun digest(data: ByteArray?, algorithm: HashAlgorithm): ByteArray {
val func : String = when (algorithm) {
HashAlgorithm.SHA1 -> "SHA-1"
HashAlgorithm.SHA224 -> "SHA-224"
HashAlgorithm.SHA256 -> "SHA-256"
HashAlgorithm.SHA384 -> "SHA-384"
HashAlgorithm.SHA512 -> "SHA-512"
}
val md = MessageDigest.getInstance(func)
return md.digest(data)
}
fun digest(data: ByteArray?, algorithm: String): ByteArray {
val func : HashAlgorithm = when (algorithm) {
"sha1" -> HashAlgorithm.SHA1
"sha224" -> HashAlgorithm.SHA224
"sha256" -> HashAlgorithm.SHA256
"sha384" -> HashAlgorithm.SHA384
"sha512" -> HashAlgorithm.SHA512
else -> HashAlgorithm.SHA256
}
return digest(data, func)
}
fun digest(data: ByteArray?): ByteArray {
return digest(data, HashAlgorithm.SHA256)
}
}

View File

@ -1,35 +0,0 @@
package fr.pointcheval.native_crypto
import android.os.Build
import java.lang.IllegalArgumentException
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
class KeyDerivation {
fun pbkdf2(password: String, salt: String, keyLength: Int, iteration: Int, algorithm: String): ByteArray {
val chars: CharArray = password.toCharArray()
val availableHashAlgorithm: Map<String, String> = mapOf(
"sha1" to "PBKDF2withHmacSHA1",
"sha224" to "PBKDF2withHmacSHA224",
"sha256" to "PBKDF2WithHmacSHA256",
"sha384" to "PBKDF2withHmacSHA384",
"sha512" to "PBKDF2withHmacSHA512"
)
if (Build.VERSION.SDK_INT >= 26) {
// SHA-1 and SHA-2 implemented
val spec = PBEKeySpec(chars, salt.toByteArray(), iteration, keyLength * 8)
val skf: SecretKeyFactory = SecretKeyFactory.getInstance(availableHashAlgorithm[algorithm]);
return skf.generateSecret(spec).encoded
} else if (Build.VERSION.SDK_INT >= 10) {
// Only SHA-1 is implemented
if (!algorithm.equals("sha1")) {
throw PlatformVersionException("Only SHA1 is implemented on this SDK version!")
}
val spec = PBEKeySpec(chars, salt.toByteArray(), iteration, keyLength * 8)
val skf: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1");
return skf.generateSecret(spec).encoded
}
throw PlatformVersionException("Invalid SDK version!")
}
}

View File

@ -1,32 +0,0 @@
package fr.pointcheval.native_crypto
import java.security.KeyPairGenerator
import java.security.SecureRandom
import javax.crypto.KeyGenerator
class KeyGeneration {
fun keygen(size : Int) : ByteArray {
val secureRandom = SecureRandom()
val keyGenerator = if (size in listOf<Int>(128,192,256)) {
KeyGenerator.getInstance("AES")
} else {
KeyGenerator.getInstance("BLOWFISH")
}
keyGenerator.init(size, secureRandom)
val sk = keyGenerator.generateKey()
return sk!!.encoded
}
fun rsaKeypairGen(size : Int) : List<ByteArray> {
val secureRandom = SecureRandom()
val keyGenerator = KeyPairGenerator.getInstance("RSA")
keyGenerator.initialize(size, secureRandom)
val keypair = keyGenerator.genKeyPair()
val res : List<ByteArray> = listOf(keypair.public.encoded, keypair.private.encoded)
return res
}
}

View File

@ -1,142 +0,0 @@
/*
* Copyright (c) 2020
* Author: Hugo Pointcheval
*/
package fr.pointcheval.native_crypto
import androidx.annotation.NonNull
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 io.flutter.plugin.common.PluginRegistry.Registrar
/** NativeCryptoPlugin */
class NativeCryptoPlugin : FlutterPlugin, MethodCallHandler {
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "native.crypto")
channel.setMethodCallHandler(NativeCryptoPlugin());
}
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "native.crypto")
channel.setMethodCallHandler(NativeCryptoPlugin())
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"digest" -> {
val message = call.argument<ByteArray>("message")
val algorithm = call.argument<String>("algorithm")
try {
val d = Hash().digest(message, algorithm!!)
if (d.isNotEmpty()) {
result.success(d)
} else {
result.error("DIGESTERROR", "DIGEST IS NULL.", null)
}
} catch (e : Exception) {
result.error("DIGESTEXCEPTION", e.message, null)
}
}
"pbkdf2" -> {
val password = call.argument<String>("password")
val salt = call.argument<String>("salt")
val keyLength = call.argument<Int>("keyLength")
val iteration = call.argument<Int>("iteration")
val algorithm = call.argument<String>("algorithm")
try {
val key = KeyDerivation().pbkdf2(password!!, salt!!, keyLength!!, iteration!!, algorithm!!)
if (key.isNotEmpty()) {
result.success(key)
} else {
result.error("PBKDF2ERROR", "PBKDF2 KEY IS NULL.", null)
}
} catch (e : Exception) {
result.error("PBKDF2EXCEPTION", e.message, null)
}
}
"keygen" -> {
val size = call.argument<Int>("size") // 128, 192, 256
try {
val key = KeyGeneration().keygen(size!!)
if (key.isNotEmpty()) {
result.success(key)
} else {
result.error("KEYGENERROR", "GENERATED KEY IS NULL.", null)
}
} catch (e : Exception) {
result.error("KEYGENEXCEPTION", e.message, null)
}
}
"rsaKeypairGen" -> {
val size = call.argument<Int>("size")
try {
val keypair = KeyGeneration().rsaKeypairGen(size!!)
if (keypair.isNotEmpty()) {
result.success(keypair)
} else {
result.error("KEYPAIRGENERROR", "GENERATED KEYPAIR IS EMPTY.", null)
}
} catch (e : Exception) {
result.error("KEYPAIRGENEXCEPTION", e.message, null)
}
}
"encrypt" -> {
val data = call.argument<ByteArray>("data")
val key = call.argument<ByteArray>("key")
val algorithm = call.argument<String>("algorithm")
val mode = call.argument<String>("mode")
val padding = call.argument<String>("padding")
try {
val payload = Cipher().encrypt(data!!, key!!, algorithm!!, mode!!, padding!!)
if (payload.isNotEmpty()) {
result.success(payload)
} else {
result.error("ENCRYPTIONERROR", "ENCRYPTED PAYLOAD IS EMPTY.", null)
}
} catch (e: Exception) {
result.error("ENCRYPTIONEXCEPTION", e.message, null)
}
}
"decrypt" -> {
val payload = call.argument<Collection<ByteArray>>("payload") // Collection<ByteArray>
val key = call.argument<ByteArray>("key")
val algorithm = call.argument<String>("algorithm")
val mode = call.argument<String>("mode")
val padding = call.argument<String>("padding")
var decryptedPayload : ByteArray? = null
try {
decryptedPayload = Cipher().decrypt(payload!!, key!!, algorithm!!, mode!!, padding!!)
if (decryptedPayload != null && decryptedPayload.isNotEmpty()) {
result.success(decryptedPayload)
} else {
result.error("DECRYPTIONERROR", "DECRYPTED PAYLOAD IS NULL. MAYBE VERIFICATION MAC IS UNVALID.", null)
}
} catch (e : Exception) {
result.error("DECRYPTIONEXCEPTION", e.message, null)
}
}
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
}
}

View File

@ -1,5 +0,0 @@
package fr.pointcheval.native_crypto
import java.lang.Exception
class PlatformVersionException(message : String) : Exception(message)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

View File

@ -1,7 +0,0 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java

View File

@ -1,12 +0,0 @@
package fr.pointcheval.native_crypto_example
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>

View File

@ -1,15 +0,0 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}

View File

@ -1 +0,0 @@
a1d04c54a9dc0dec55298921eadf7972

View File

@ -1,90 +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 parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
generated_key_values = {}
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Invalid plugin specification: #{line}"
end
end
generated_key_values
end
target 'Runner' do
use_frameworks!
use_modular_headers!
# Flutter Pod
copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
end
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end

View File

@ -1,22 +0,0 @@
PODS:
- Flutter (1.0.0)
- native_crypto (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- native_crypto (from `.symlinks/plugins/native_crypto/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
native_crypto:
:path: ".symlinks/plugins/native_crypto/ios"
SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
native_crypto: 33b8108e3fcc10052862b69863efc2304c59cb2f
PODFILE CHECKSUM: 1b66dae606f75376c5f2135a8290850eeb09ae83
COCOAPODS: 1.10.1

View File

@ -1 +0,0 @@
#import "GeneratedPluginRegistrant.h"

View File

@ -1,69 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:flutter/material.dart';
import 'package:native_crypto_example/pages/kemPage.dart';
import 'pages/benchmarkPage.dart';
import 'pages/cipherPage.dart';
import 'pages/hashKeyDerivationPage.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _currentIndex = 0;
final List<Widget> _children = [
HashKeyDerivationPage(),
CipherPage(),
KemPage(),
BenchmarkPage(),
];
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: 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: [
BottomNavigationBarItem(
icon: Icon(Icons.vpn_key),
label: 'Key',
),
BottomNavigationBarItem(
icon: Icon(Icons.lock),
label: 'Encryption',
),
BottomNavigationBarItem(
icon: Icon(Icons.connect_without_contact),
label: 'KEM',
),
BottomNavigationBarItem(
icon: Icon(Icons.timer),
label: 'Benchmark',
),
],
),
),
);
}
}

View File

@ -1,144 +0,0 @@
// Copyright (c) 2021
// Author: Hugo Pointcheval
import 'dart:developer';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:native_crypto/native_crypto.dart';
import '../session.dart';
import '../widgets/button.dart';
import '../widgets/output.dart';
class BenchmarkPage extends StatefulWidget {
const BenchmarkPage({key}) : super(key: key);
@override
_BenchmarkPageState createState() => _BenchmarkPageState();
}
class _BenchmarkPageState extends State<BenchmarkPage> {
final Output keyContent = Output(
textEditingController: TextEditingController(),
);
final Output benchmarkStatus = Output(
textEditingController: TextEditingController(),
large: true,
);
Future<void> _benchmark() async {
if (Session.secretKey == null || Session.secretKey.isEmpty) {
benchmarkStatus
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
return;
} else if (!Session.aesCipher.isInitialized) {
benchmarkStatus.print(
'Cipher not initialized!\nGo in Key tab and generate or derive one.');
return;
}
benchmarkStatus.print("Benchmark 2/4/8/16/32/64/128/256MB\n");
List<int> testedSizes = [2, 4, 8, 16, 32, 64, 128, 256];
String csv =
"size;encryption time;encode time;decryption time;crypto time\n";
var beforeBench = DateTime.now();
for (int size in testedSizes) {
var bigFile = Uint8List(size * 1000000);
csv += "${size * 1000000};";
var cryptoTime = 0;
// Encryption
var before = DateTime.now();
var encryptedBigFile = await Session.aesCipher.encrypt(bigFile);
var after = DateTime.now();
var benchmark =
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
benchmarkStatus.append('[$size MB] Encryption took $benchmark ms\n');
csv += "$benchmark;";
cryptoTime += benchmark;
// Encoding
before = DateTime.now();
encryptedBigFile.encode();
after = DateTime.now();
benchmark = after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
benchmarkStatus.append('[$size MB] Encoding took $benchmark ms\n');
csv += "$benchmark;";
// Decryption
before = DateTime.now();
await Session.aesCipher.decrypt(encryptedBigFile);
after = DateTime.now();
benchmark = after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
benchmarkStatus.append('[$size MB] Decryption took $benchmark ms\n');
csv += "$benchmark;";
cryptoTime += benchmark;
csv += "$cryptoTime\n";
}
var afterBench = DateTime.now();
var benchmark =
afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch;
var sum = testedSizes.reduce((a, b) => a + b);
benchmarkStatus.append(
'Benchmark finished.\nGenerated, encrypted and decrypted $sum MB in $benchmark ms');
log(csv, name: "Benchmark");
}
void _clear() {
benchmarkStatus.clear();
}
@override
void initState() {
super.initState();
if (Session.secretKey != null) {
keyContent.print(Session.secretKey.encoded.toString());
Session.aesCipher = AESCipher(
Session.secretKey,
CipherParameters(
BlockCipherMode.CBC,
PlainTextPadding.PKCS5,
),
);
}
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Align(
child: Text("Secret Key"),
alignment: Alignment.centerLeft,
),
keyContent,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Button(
onPressed: _benchmark,
label: "Launch benchmark",
),
Button(
onPressed: _clear,
label: "Clear",
),
],
),
benchmarkStatus,
],
),
),
);
}
}

View File

@ -1,205 +0,0 @@
// Copyright (c) 2021
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:native_crypto/native_crypto.dart';
import '../session.dart';
import '../utils.dart';
import '../widgets/button.dart';
import '../widgets/output.dart';
class CipherPage extends StatefulWidget {
const CipherPage({key}) : super(key: key);
@override
_CipherPageState createState() => _CipherPageState();
}
class _CipherPageState extends State<CipherPage> {
final Output keyContent = Output(
textEditingController: TextEditingController(),
);
final Output encryptionStatus = Output(
textEditingController: TextEditingController(),
);
final Output decryptionStatus = Output(
textEditingController: TextEditingController(),
);
final Output cipherExport = Output(
textEditingController: TextEditingController(),
large: true,
editable: true,
);
final TextEditingController _plainTextController = TextEditingController();
CipherText cipherText;
void _encrypt() async {
final plainText = _plainTextController.text.trim();
if (Session.secretKey == null || Session.secretKey.isEmpty) {
encryptionStatus
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
} else if (!Session.aesCipher.isInitialized) {
encryptionStatus.print(
'Cipher not initialized!\nGo in Key tab and generate or derive one.');
} else if (plainText.isEmpty) {
encryptionStatus.print('Entry is empty');
} else {
var stringToBytes = TypeHelper.stringToBytes(plainText);
cipherText = await Session.aesCipher.encrypt(stringToBytes);
encryptionStatus.print('String successfully encrypted.\n');
encryptionStatus.append("IV: " +
cipherText.iv.toString() +
"\nCipherText: " +
cipherText.bytes.toString());
}
}
void _alter() async {
if (cipherText == null || cipherText.bytes.isEmpty) {
decryptionStatus.print('Encrypt before altering CipherText!');
} else {
// Add 1 to the first byte
List<Uint8List> _altered = cipherText.bytes;
_altered[0][0] += 1;
// Recreate cipher text with altered data
cipherText = AESCipherText.from(_altered, cipherText.iv);
encryptionStatus.print('String successfully encrypted.\n');
encryptionStatus.append("IV: " +
cipherText.iv.toString() +
"\nCipherText: " +
cipherText.bytes.toString());
decryptionStatus.print('CipherText altered!\nDecryption will fail.');
}
}
void _decrypt() async {
if (Session.secretKey == null || Session.secretKey.isEmpty) {
decryptionStatus
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
} else if (!Session.aesCipher.isInitialized) {
decryptionStatus.print(
'Cipher not initialized!\nGo in Key tab and generate or derive one.');
} else if (cipherText == null || cipherText.bytes.isEmpty) {
decryptionStatus.print('Encrypt before decrypting!');
} else {
try {
Uint8List plainText = await Session.aesCipher.decrypt(cipherText);
var bytesToString = TypeHelper.bytesToString(plainText);
decryptionStatus
.print('String successfully decrypted:\n\n$bytesToString');
} on DecryptionException catch (e) {
decryptionStatus.print(e.message);
}
}
}
void _export() async {
if (cipherText == null) {
decryptionStatus.print('Encrypt data before export!');
} else {
// TODO: fix export format to support chunks !
Uint8List payload = cipherText.encode();
String data = TypeHelper.bytesToBase64(payload);
decryptionStatus.print('CipherText successfully exported');
cipherExport.print(data);
}
}
void _import() async {
final String data = cipherExport.read();
if (data.isEmpty) {
encryptionStatus.print('CipherText import failed');
} else {
Uint8List payload = TypeHelper.base64ToBytes(data);
cipherText = AESCipherText.empty();
cipherText.decode(payload);
encryptionStatus.print('CipherText successfully imported\n');
encryptionStatus.append("IV: " +
cipherText.iv.toString() +
"\nCipherText: " +
cipherText.bytes.toString());
}
}
@override
void initState() {
super.initState();
if (Session.secretKey != null) {
keyContent.print(Session.secretKey.encoded.toString());
Session.aesCipher = AESCipher(
Session.secretKey,
CipherParameters(
BlockCipherMode.CBC,
PlainTextPadding.PKCS5,
),
);
}
}
@override
void dispose() {
super.dispose();
_plainTextController.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Align(
child: Text("Secret Key"),
alignment: Alignment.centerLeft,
),
keyContent,
TextField(
controller: _plainTextController,
decoration: InputDecoration(
hintText: 'Plain text',
),
),
Button(
onPressed: _encrypt,
label: "Encrypt",
),
encryptionStatus,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Button(
onPressed: _alter,
label: "Alter cipher",
),
Button(
onPressed: _decrypt,
label: "Decrypt",
),
],
),
decryptionStatus,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Button(
onPressed: _export,
label: "Export cipher",
),
Button(
onPressed: _import,
label: "Import cipher",
),
],
),
cipherExport
],
),
),
);
}
}

View File

@ -1,180 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:native_crypto/native_crypto.dart';
import '../session.dart';
import '../utils.dart';
import '../widgets/button.dart';
import '../widgets/output.dart';
class HashKeyDerivationPage extends StatefulWidget {
const HashKeyDerivationPage({key}) : super(key: key);
@override
_HashKeyDerivationPageState createState() => _HashKeyDerivationPageState();
}
class _HashKeyDerivationPageState extends State<HashKeyDerivationPage> {
final Output keyContent = Output(
textEditingController: TextEditingController(),
);
final Output keyStatus = Output(
textEditingController: TextEditingController(),
);
final Output keyExport = Output(
textEditingController: TextEditingController(),
large: true,
editable: true,
);
final Output pbkdf2Status = Output(
textEditingController: TextEditingController(),
);
final Output hashStatus = Output(
textEditingController: TextEditingController(),
);
final TextEditingController _pwdTextController = TextEditingController();
final TextEditingController _messageTextController = TextEditingController();
final TextEditingController _keyTextController = TextEditingController();
void _generate() async {
try {
Session.secretKey = await SecretKey.generate(256, CipherAlgorithm.AES);
keyContent.print(Session.secretKey.encoded.toString());
keyStatus.print(
"Secret Key successfully generated.\nLength: ${Session.secretKey.encoded.length} bytes");
} catch (e) {
keyStatus.print(e.message);
}
}
void _pbkdf2() async {
final password = _pwdTextController.text.trim();
if (password.isEmpty) {
pbkdf2Status.print('Password is empty');
} else {
PBKDF2 _pbkdf2 =
PBKDF2(keyLength: 32, iteration: 1000, hash: HashAlgorithm.SHA512);
await _pbkdf2.derive(password: password, salt: 'salty');
SecretKey key = _pbkdf2.key;
pbkdf2Status.print('Key successfully derived.');
Session.secretKey = key;
keyContent.print(Session.secretKey.encoded.toString());
}
}
void _export() async {
if (Session.secretKey == null || Session.secretKey.isEmpty) {
keyStatus
.print('No SecretKey!\nGenerate or derive one before exporting!');
} else {
String key = TypeHelper.bytesToBase64(Session.secretKey.encoded);
keyStatus.print('Key successfully exported');
keyExport.print(key);
}
}
void _import() async {
final String key = keyExport.read();
if (key.isEmpty) {
keyStatus.print('Key import failed');
} else {
Uint8List keyBytes = TypeHelper.base64ToBytes(key);
Session.secretKey =
SecretKey.fromBytes(keyBytes, algorithm: CipherAlgorithm.AES);
keyStatus.print('Key successfully imported');
keyContent.print(Session.secretKey.encoded.toString());
}
}
void _hash() async {
final message = _messageTextController.text.trim();
if (message.isEmpty) {
hashStatus.print('Message is empty');
} else {
MessageDigest md = MessageDigest.getInstance("sha256");
Uint8List hash = await md.digest(TypeHelper.stringToBytes(message));
hashStatus.print('Message successfully hashed.\n' + hash.toString());
}
}
@override
void initState() {
super.initState();
if (Session.secretKey != null) {
keyContent.print(Session.secretKey.encoded.toString());
}
}
@override
void dispose() {
super.dispose();
_pwdTextController.dispose();
_messageTextController.dispose();
_keyTextController.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Align(
child: Text("Secret Key"),
alignment: Alignment.centerLeft,
),
keyContent,
Button(
onPressed: _generate,
label: "Generate key",
),
keyStatus,
TextField(
controller: _pwdTextController,
decoration: InputDecoration(
hintText: 'Password',
),
),
Button(
onPressed: _pbkdf2,
label: "Apply PBKDF2",
),
pbkdf2Status,
TextField(
controller: _messageTextController,
decoration: InputDecoration(
hintText: 'Message',
),
),
Button(
onPressed: _hash,
label: "Hash",
),
hashStatus,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Button(
onPressed: _export,
label: "Export key",
),
Button(
onPressed: _import,
label: "Import key",
),
],
),
keyExport
],
),
),
);
}
}

View File

@ -1,36 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:flutter/material.dart';
import 'package:native_crypto/native_crypto.dart';
class KemPage extends StatefulWidget {
KemPage({key}) : super(key: key);
@override
_KemPageState createState() => _KemPageState();
}
class _KemPageState extends State<KemPage> {
void test() async {
KeySpec specs = RSAKeySpec(2048);
KeyPair kp = await KeyPair.generate(specs);
print(kp.isComplete);
print(kp.privateKey);
print(kp.privateKey.encoded);
}
@override
void initState() {
super.initState();
test();
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("Not implemented."),
),
);
}
}

View File

@ -1,9 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:native_crypto/native_crypto.dart';
class Session {
static SecretKey secretKey;
static AESCipher aesCipher;
}

View File

@ -1,30 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'dart:convert';
/// Contains some useful functions.
class TypeHelper {
/// Returns bytes [Uint8List] from a [String].
static Uint8List stringToBytes(String source) {
var list = source.runes.toList();
var bytes = Uint8List.fromList(list);
return bytes;
}
/// Returns a [String] from bytes [Uint8List].
static String bytesToString(Uint8List bytes) {
var string = String.fromCharCodes(bytes);
return string;
}
/// Returns a `base64` [String] from bytes [Uint8List].
static String bytesToBase64(Uint8List bytes) {
return base64.encode(bytes);
}
/// Returns a [Uint8List] from a `base64` [String].
static Uint8List base64ToBytes(String encoded) {
return base64.decode(encoded);
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:flutter/material.dart';
class Button extends StatelessWidget {
const Button({Key key, this.onPressed, this.label}) : super(key: key);
final void Function() onPressed;
final String label;
@override
Widget build(BuildContext context) {
return Container(
child: FlatButton(
onPressed: onPressed,
color: Colors.blue,
child: Text(
label,
style: TextStyle(color: Colors.white),
),
),
);
}
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:developer';
import 'package:flutter/material.dart';
class Output extends StatelessWidget {
const Output(
{Key key,
this.textEditingController,
this.large: false,
this.editable: false})
: super(key: key);
final TextEditingController textEditingController;
final bool large;
final bool editable;
void print(String message) {
log(message, name: "NativeCrypto Example");
textEditingController.text = message;
}
void append(String message) {
log(message, name: "NativeCrypto Example");
textEditingController.text += message;
}
void appendln(String message) {
log(message, name: "NativeCrypto Example");
textEditingController.text += message + "\n";
}
void clear() {
textEditingController.clear();
}
String read() {
return textEditingController.text;
}
@override
Widget build(BuildContext context) {
return Container(
child: TextField(
enableInteractiveSelection: true,
readOnly: editable ? false : true,
minLines: large ? 3 : 1,
maxLines: large ? 500 : 5,
decoration: InputDecoration(border: OutlineInputBorder()),
controller: textEditingController,
),
);
}
}

View File

@ -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(MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data.startsWith('Running on:'),
),
findsOneWidget,
);
});
}

View File

@ -1,135 +0,0 @@
//
// Cipher.swift
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Foundation
import CommonCrypto
enum CipherAlgorithm: String {
case AES = "aes"
case BlowFish = "blowfish"
var instance: Int {
switch self {
case .AES: return kCCAlgorithmAES
case .BlowFish: return kCCAlgorithmBlowfish
}
}
}
enum BlockCipherMode: String {
case ECB = "ecb"
case CBC = "cbc"
var instance: Int {
switch self {
case .CBC: return 0
case .ECB: return kCCOptionECBMode
}
}
}
enum Padding: String {
case PKCS5 = "pkcs5"
case None = "none"
var instance: Int {
switch self {
case .PKCS5: return kCCOptionPKCS7Padding
case .None: return 0
}
}
}
class Cipher {
func encrypt(data : Data, key : Data, algorithm : CipherAlgorithm, mode : BlockCipherMode, padding : Padding) -> [Data]? {
// Calculate Mac
let mac = Hash().digest(data: key + data, algorithm: .SHA256)
let payload = mac! + data
// Generate IV
let ivBytes = UnsafeMutableRawPointer.allocate(byteCount: kCCBlockSizeAES128, alignment: 1)
defer { ivBytes.deallocate() }
let ivStatus = CCRandomGenerateBytes(ivBytes, kCCBlockSizeAES128)
if (ivStatus != kCCSuccess) {
return nil
}
let ivData = Data(bytes: ivBytes, count: kCCBlockSizeAES128)
let algo = algorithm.instance
let options: CCOptions = UInt32(mode.instance + padding.instance)
guard var ciphertext = crypt(operation: kCCEncrypt,
algorithm: algo,
options: options,
key: key,
initializationVector: ivData,
dataIn: payload) else { return nil }
return [ciphertext, ivData]
}
func decrypt(payload : [Data], key : Data, algorithm : CipherAlgorithm, mode : BlockCipherMode, padding : Padding) -> Data? {
let encrypted = payload[1] + payload[0]
guard encrypted.count > kCCBlockSizeAES128 else { return nil }
let iv = encrypted.prefix(kCCBlockSizeAES128)
let ciphertext = encrypted.suffix(from: kCCBlockSizeAES128)
let algo = algorithm.instance
let options: CCOptions = UInt32(mode.instance + padding.instance)
guard var decrypted = crypt(operation: kCCDecrypt,
algorithm: algo,
options: options,
key: key,
initializationVector: iv,
dataIn: ciphertext) else {return nil}
// Create a range based on the length of data to return
let range = 0..<32
// Get a new copy of data
let mac = decrypted.subdata(in: range)
decrypted.removeSubrange(range)
let vmac = Hash().digest(data: key + decrypted, algorithm: .SHA256)
if (mac.base64EncodedData() == vmac!.base64EncodedData()) {
return decrypted
} else {
return nil
}
}
private func crypt(operation: Int, algorithm: Int, options: UInt32, key: Data,
initializationVector: Data, dataIn: Data) -> Data? {
return key.withUnsafeBytes { keyUnsafeRawBufferPointer in
return dataIn.withUnsafeBytes { dataInUnsafeRawBufferPointer in
return initializationVector.withUnsafeBytes { ivUnsafeRawBufferPointer in
let dataOutSize: Int = dataIn.count + kCCBlockSizeAES128*2
let dataOut = UnsafeMutableRawPointer.allocate(byteCount: dataOutSize,
alignment: 1)
defer { dataOut.deallocate() }
var dataOutMoved: Int = 0
let status = CCCrypt(CCOperation(operation), CCAlgorithm(algorithm),
CCOptions(options),
keyUnsafeRawBufferPointer.baseAddress, key.count,
ivUnsafeRawBufferPointer.baseAddress,
dataInUnsafeRawBufferPointer.baseAddress, dataIn.count,
dataOut, dataOutSize, &dataOutMoved)
guard status == kCCSuccess else { return nil }
return Data(bytes: dataOut, count: dataOutMoved)
}
}
}
}
}

View File

@ -1,74 +0,0 @@
//
// Hash.swift
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Foundation
import CommonCrypto
enum HashAlgorithm: String {
case SHA1 = "sha1"
case SHA224 = "sha224"
case SHA256 = "sha256"
case SHA384 = "sha384"
case SHA512 = "sha512"
var digestLength: Int {
switch self {
case .SHA1: return Int(CC_SHA1_DIGEST_LENGTH)
case .SHA224: return Int(CC_SHA224_DIGEST_LENGTH)
case .SHA256: return Int(CC_SHA256_DIGEST_LENGTH)
case .SHA384: return Int(CC_SHA384_DIGEST_LENGTH)
case .SHA512: return Int(CC_SHA512_DIGEST_LENGTH)
}
}
var pbkdf2: UInt32 {
switch self {
case .SHA1: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA1)
case .SHA224: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA224)
case .SHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256)
case .SHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384)
case .SHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512)
}
}
}
class Hash {
func digest(data: Data?, algorithm: HashAlgorithm) -> Data? {
if (data == nil) {
return nil
}
let hashBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: algorithm.digestLength)
defer { hashBytes.deallocate() }
switch algorithm {
case .SHA1:
data!.withUnsafeBytes { (buffer) -> Void in
CC_SHA1(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
case .SHA224:
data!.withUnsafeBytes { (buffer) -> Void in
CC_SHA224(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
case .SHA256:
data!.withUnsafeBytes { (buffer) -> Void in
CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
case .SHA384:
data!.withUnsafeBytes { (buffer) -> Void in
CC_SHA384(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
case .SHA512:
data!.withUnsafeBytes { (buffer) -> Void in
CC_SHA512(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
}
return Data(bytes: hashBytes, count: algorithm.digestLength)
}
}

View File

@ -1,40 +0,0 @@
//
// KeyDerivation.swift
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Foundation
import CommonCrypto
class KeyDerivation {
func pbkdf2(password: String, salt: String, keyLength: Int, iteration: Int, algorithm: HashAlgorithm) -> Data? {
let passwordData = password.data(using: .utf8)!
let saltData = salt.data(using: .utf8)!
var derivedKeyData = Data(repeating: 0, count: keyLength)
var localDerivedKeyData = derivedKeyData
let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in
saltData.withUnsafeBytes { saltBytes in
CCKeyDerivationPBKDF(
CCPBKDFAlgorithm(kCCPBKDF2),
password, passwordData.count,
saltBytes, saltData.count,
algorithm.pbkdf2,
UInt32(iteration),
derivedKeyBytes, localDerivedKeyData.count)
}
}
if (derivationStatus != kCCSuccess) {
print("Error: \(derivationStatus)")
return nil;
}
return derivedKeyData
}
}

View File

@ -1,59 +0,0 @@
//
// KeyGeneration.swift
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Foundation
import CommonCrypto
class KeyGeneration {
func keygen(size : NSNumber) -> Data? {
var bytes = [Int8](repeating: 0, count: size.intValue / 8)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
if status == errSecSuccess {
let keyBytes = bytes.withUnsafeBytes {
return Data(Array($0))
}
return keyBytes
}
return nil
}
@available(iOS 10.0, watchOS 3.0, tvOS 10.0, *)
func rsaKeypairGen(size : NSNumber) throws -> [Data]? {
let tagData = UUID().uuidString.data(using: .utf8)
let isPermanent = true
let attributes: [CFString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: (size.intValue * 8),
kSecPrivateKeyAttrs: [
kSecAttrIsPermanent: isPermanent,
kSecAttrApplicationTag: tagData!
]
]
var error: Unmanaged<CFError>?
guard let privKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let pubKey = SecKeyCopyPublicKey(privKey)
var errorExport: Unmanaged<CFError>?
let data1 = SecKeyCopyExternalRepresentation(pubKey!, &errorExport)
let unwrappedData1 = data1 as Data?
let data2 = SecKeyCopyExternalRepresentation(privKey, &errorExport)
let unwrappedData2 = data2 as Data?
return [unwrappedData1!, unwrappedData2!]
}
}

View File

@ -1,4 +0,0 @@
#import <Flutter/Flutter.h>
@interface NativeCryptoPlugin : NSObject<FlutterPlugin>
@end

View File

@ -1,158 +0,0 @@
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Flutter
extension FlutterStandardTypedData {
var uint8Array: Array<UInt8> {
return Array(data)
}
var int8Array: Array<Int8> {
return data.withUnsafeBytes { raw in
[Int8](raw.bindMemory(to: Int8.self))
}
}
}
public class SwiftNativeCryptoPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "native.crypto", binaryMessenger: registrar.messenger())
let instance = SwiftNativeCryptoPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "digest":
let args = call.arguments as! NSDictionary
let message = (args["message"] as! FlutterStandardTypedData).data
let algo = args["algorithm"] as! String
let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo)
let hash = Hash().digest(data: message, algorithm: algorithm!)
if hash != nil {
result(FlutterStandardTypedData.init(bytes: hash!))
} else {
result(FlutterError(code: "DIGESTERROR",
message: "DIGEST IS NIL.",
details: nil)
)
}
case "pbkdf2":
let args = call.arguments as! NSDictionary
let password = args["password"] as! String
let salt = args["salt"] as! String
let keyLength = args["keyLength"] as! NSNumber
let iteration = args["iteration"] as! NSNumber
let algo = args["algorithm"] as! String
let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo)
let key = KeyDerivation().pbkdf2(password: password, salt: salt, keyLength: keyLength.intValue, iteration: iteration.intValue, algorithm: algorithm!)
if key != nil {
result(FlutterStandardTypedData.init(bytes: key!))
} else {
result(FlutterError(code: "PBKDF2ERROR",
message: "PBKDF2 KEY IS NIL.",
details: nil)
)
}
case "keygen":
let args = call.arguments as! NSDictionary
let size = args["size"] as! NSNumber
let key = KeyGeneration().keygen(size: size)
if key != nil {
result(FlutterStandardTypedData.init(bytes: key!))
} else {
result(FlutterError(code: "KEYGENERROR",
message: "GENERATED KEY IS NIL.",
details: nil))
}
case "rsaKeypairGen":
let args = call.arguments as! NSDictionary
let size = args["size"] as! NSNumber
let keypair : [Data]?
if #available(iOS 10.0, *) {
do {
keypair = try KeyGeneration().rsaKeypairGen(size: size)
} catch {
keypair = nil
}
} else {
// Fallback on earlier versions
keypair = nil
}
if keypair != nil {
result(keypair)
} else {
result(FlutterError(code: "KEYPAIRGENERROR",
message: "GENERATED KEYPAIR IS EMPTY.",
details: nil))
}
case "encrypt":
let args = call.arguments as! NSDictionary
let data = (args["data"] as! FlutterStandardTypedData).data
let key = (args["key"] as! FlutterStandardTypedData).data
let algo = args["algorithm"] as! String
let mode = args["mode"] as! String
let padding = args["padding"] as! String
let algorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algo)
let modeEnum : BlockCipherMode? = BlockCipherMode.init(rawValue: mode)
let paddingEnum : Padding? = Padding.init(rawValue: padding)
let ciphertext = Cipher().encrypt(data: data, key: key, algorithm: algorithm!, mode: modeEnum!, padding: paddingEnum!)
if ciphertext != nil {
result(ciphertext)
} else {
result(FlutterError(code: "ENCRYPTIONERROR",
message: "ENCRYPTED PAYLOAD IS EMPTY.",
details: nil))
}
case "decrypt":
let args = call.arguments as! NSDictionary
let payload = args["payload"] as! NSArray
let key = (args["key"] as! FlutterStandardTypedData).data
let algo = args["algorithm"] as! String
let mode = args["mode"] as! String
let padding = args["padding"] as! String
let encrypted = (payload[0] as! FlutterStandardTypedData).data
let iv = (payload[1] as! FlutterStandardTypedData).data
let encryptedPayload = [encrypted, iv]
let algorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algo)
let modeEnum : BlockCipherMode? = BlockCipherMode.init(rawValue: mode)
let paddingEnum : Padding? = Padding.init(rawValue: padding)
let decrypted = Cipher().decrypt(payload: encryptedPayload, key: key, algorithm: algorithm!, mode: modeEnum!, padding: paddingEnum!)
if decrypted != nil {
result(FlutterStandardTypedData.init(bytes: decrypted!))
} else {
result(FlutterError(code: "DECRYPTIONERROR",
message: "DECRYPTED PAYLOAD IS NIL. MAYBE VERIFICATION MAC IS UNVALID.",
details: nil))
}
default: result(FlutterMethodNotImplemented)
}
}
}

View File

@ -1,19 +0,0 @@
// Copyright (c) 2021
// Author: Hugo Pointcheval
export './src/exceptions.dart';
export './src/key.dart';
export './src/keyspec.dart';
export './src/keyderivation.dart';
export './src/digest.dart';
export './src/cipher.dart';
export './src/kem.dart';
export './src/platform.dart';
export './src/utils.dart';
export './src/sym/AES.dart';
export './src/asym/RSA.dart';
const String version = "0.0.6";
const String author = "Hugo Pointcheval";
const String website = "https://hugo.pointcheval.fr/";
const String repository = "https://github.com/hugo-pcl/native-crypto-flutter";

View File

@ -1,77 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:native_crypto/src/exceptions.dart';
import 'package:native_crypto/src/kem.dart';
import 'package:native_crypto/src/key.dart';
/// This represents RSA Key Encapsulation Mechanism
class RSAKeyEncapsulationMechanism implements KeyEncapsulationMechanism {
bool _isInit = false;
KemMode _mode;
PublicKey _publicKey;
PrivateKey _privateKey;
@override
KemAlgorithm get algorithm => KemAlgorithm.RSA;
@override
Object get options => null;
@override
bool get isInitialized => _isInit;
@override
KemMode get mode => _mode;
@override
KeyPair get keypair => KeyPair.from(_publicKey, _privateKey);
RSAKeyEncapsulationMechanism(
KemMode mode, {
KeyPair keyPair,
PublicKey publicKey,
}) {
_mode = mode;
if (_mode == KemMode.ENCAPSULATION) {
if (publicKey != null) {
_isInit = true;
_publicKey = publicKey;
} else if (keyPair != null) {
if (keyPair.publicKey != null) {
_isInit = true;
_publicKey = keyPair.publicKey;
}
}
} else if (_mode == KemMode.DECAPSULATION) {
if (keyPair != null) {
if (keyPair.isComplete) {
_isInit = true;
_publicKey = keyPair.publicKey;
_privateKey = keyPair.privateKey;
} else {
throw KemInitException("Keypair must be complete for decapsulation.");
}
} else {
throw KemInitException("You must provide a keypair for decapsulation.");
}
}
}
@override
Future<Encapsulation> encapsulate() {
if (!_isInit || _mode == KemMode.DECAPSULATION) {
throw KemInitException("KEM not properly initialized.");
}
throw UnimplementedError();
}
@override
Future<SecretKey> decapsulate(Encapsulation encapsulation) {
if (!_isInit || _mode == KemMode.ENCAPSULATION) {
throw KemInitException("KEM not properly initialized.");
}
throw UnimplementedError();
}
}

View File

@ -1,101 +0,0 @@
// Copyright (c) 2021
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'key.dart';
/// Represents different cipher algorithms
enum CipherAlgorithm { AES, None }
/// Represents different block cipher modes
enum BlockCipherMode { CBC, GCM }
/// Represents different padding
enum PlainTextPadding { PKCS5, None }
/// Represents a cipher.
///
/// 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.
abstract class Cipher {
/// Returns the standard algorithm name for this cipher
CipherAlgorithm get algorithm;
/// Returns the secret key used for this cipher
SecretKey get secretKey;
/// Returns the parameters used for this cipher
CipherParameters get parameters;
/// Returns true if cipher is initialized
bool get isInitialized;
/// Returnes list of supported [CipherParameters] for this cipher
List<CipherParameters> get supportedParameters;
/// Encrypts data.
///
/// Takes [Uint8List] data as parameter.
/// Returns a [CipherText].
Future<CipherText> encrypt(Uint8List data);
/// Decrypts cipher text.
///
/// Takes [CipherText] as parameter.
/// And returns plain text data as [Uint8List].
Future<Uint8List> decrypt(CipherText cipherText);
}
/// Represents a cipher text.
///
/// It's the result of an encryption.
abstract class CipherText {
/// Returns the standard algorithm name used for this ciphertext.
CipherAlgorithm get algorithm;
/// Returns the data of this ciphertext (in chunks).
List<Uint8List> get bytes;
/// Returns the IV of this cipertext (in chunks).
List<Uint8List> get iv;
/// Returns the chunk number of this cipherText.
int get size;
/// Returns this ciphertext in simple Byte Array format.
Uint8List encode();
/// Transforms a simple Byte Array to a NativeCrypto cipherText.
void decode(Uint8List src);
}
/// Represents a pair of [BlockCipherMode] and [Padding]
class CipherParameters {
BlockCipherMode _mode;
PlainTextPadding _padding;
/// Returns mode used in the cipher
BlockCipherMode get mode => _mode;
/// Returns padding used in the cipher
PlainTextPadding get padding => _padding;
CipherParameters(BlockCipherMode mode, PlainTextPadding padding) {
_mode = mode;
_padding = padding;
}
@override
bool operator ==(Object o) {
if (identical(this, o)) return true;
return o is CipherParameters &&
o._mode.index == _mode.index &&
o._padding.index == _padding.index;
}
@override
int get hashCode => _mode.hashCode ^ _padding.hashCode;
}

View File

@ -1,41 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'exceptions.dart';
import 'platform.dart';
import 'utils.dart';
enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 }
/// Represents message digest, or hash function.
class MessageDigest {
HashAlgorithm _algo;
/// Returns the standard algorithm name for this digest
HashAlgorithm get algorithm => _algo;
/// Returns true if digest is initialized
bool get isInitialized => (_algo != null);
/// Creates [MessageDigest] with a specific algorithm
MessageDigest(HashAlgorithm algorithm) {
_algo = algorithm;
}
/// Creates [MessageDigest] from the name of an algorithm
MessageDigest.getInstance(String algorithm) {
_algo = Utils.getHashAlgorithm(algorithm);
}
/// Hashes a message
Future<Uint8List> digest(Uint8List data) async {
if (!isInitialized) {
throw DigestInitException('Digest not properly initialized.');
}
Uint8List hash = await Platform().digest(data, _algo);
return hash;
}
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
class NativeCryptoException implements Exception {
String message;
NativeCryptoException(this.message);
}
class UtilsException extends NativeCryptoException {
UtilsException(message) : super(message);
}
class KeyException extends NativeCryptoException {
KeyException(message) : super(message);
}
class KeyGenerationException extends NativeCryptoException {
KeyGenerationException(message) : super(message);
}
class KeyPairGenerationException extends NativeCryptoException {
KeyPairGenerationException(message) : super(message);
}
class KeyDerivationException extends NativeCryptoException {
KeyDerivationException(message) : super(message);
}
class CipherInitException extends NativeCryptoException {
CipherInitException(message) : super(message);
}
class KemInitException extends NativeCryptoException {
KemInitException(message) : super(message);
}
class DigestInitException extends NativeCryptoException {
DigestInitException(message) : super(message);
}
class DigestException extends NativeCryptoException {
DigestException(message) : super(message);
}
class EncryptionException extends NativeCryptoException {
EncryptionException(message) : super(message);
}
class DecryptionException extends NativeCryptoException {
DecryptionException(message) : super(message);
}
class NotImplementedException extends NativeCryptoException {
NotImplementedException(message) : super(message);
}

View File

@ -1,52 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'key.dart';
enum KemAlgorithm { RSA, ECDH }
enum KemMode { ENCAPSULATION, DECAPSULATION }
abstract class KeyEncapsulationMechanism {
/// Returns the standard algorithm name for this kem
KemAlgorithm get algorithm;
/// Returns the parameters used for this kem
Object get options;
/// Returns true if kem is initialized
bool get isInitialized;
/// Returns mode used in this kem.
KemMode get mode;
/// Returns key pair used in this kem.
///
/// Can be an incomplete if just have public key
/// for example.
KeyPair get keypair;
/// Encapsulate key.
///
/// Returns an [Encapsulation].
Future<Encapsulation> encapsulate();
/// Decapsulate key.
///
/// Takes [Encapsulation] as parameter.
/// And returns plain text key as [SecretKey].
Future<SecretKey> decapsulate(Encapsulation encapsulation);
}
abstract class Encapsulation {
/// Returns the standard algorithm name used for this encapsulation
KemAlgorithm get algorithm;
/// Returns the secret key used in this encapsulation
SecretKey get secretKey;
/// Returns the encapsulated key
Uint8List get key;
}

View File

@ -1,118 +0,0 @@
// Copyright (c) 2021
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'platform.dart';
import 'exceptions.dart';
import 'keyspec.dart';
import 'cipher.dart';
import 'utils.dart';
/// This is the base class of all key types.
abstract class Key {
Uint8List _bytes;
String _algo;
/// Returns key as raw byte array.
Uint8List get encoded => _bytes;
/// Returns the standard algorithm name for this key
String get algorithm => _algo;
/// Returns the true if this key is null or empty.
bool get isEmpty => (_bytes == null || _bytes.length == 0);
Key({Uint8List bytes, String algorithm}) {
_bytes = bytes;
_algo = algorithm;
}
}
/// This represents a secret key, usefull in
/// algorithms like AES or BlowFish.
class SecretKey extends Key {
/// Creates a key from raw byte array
SecretKey.fromBytes(Uint8List bytes,
{CipherAlgorithm algorithm: CipherAlgorithm.None})
: super(bytes: bytes, algorithm: algorithm.name);
/// Creates a key from a specific size.
static Future<SecretKey> generate(int size, CipherAlgorithm algorithm) async {
if (algorithm == null) {
throw KeyException("Algorithm can't be null");
} else if (algorithm == CipherAlgorithm.AES) {
List<int> _supportedSizes = [128, 192, 256];
if (!_supportedSizes.contains(size)) {
throw KeyException("AES must be 128, 192 or 256 bits long.");
}
}
try {
Uint8List _key = await Platform().keygen(size);
return SecretKey.fromBytes(_key, algorithm: algorithm);
} on PlatformException catch (e) {
throw KeyException(e);
}
}
}
/// This represents a keypair, usefull in
/// algorithms like RSA.
class KeyPair extends Key {
PublicKey _publicKey;
PrivateKey _privateKey;
/// Returns public key of this key pair.
PublicKey get publicKey => _publicKey;
/// Returns private key of this key pair.
PrivateKey get privateKey => _privateKey;
/// Returns true if key pair contains public AND private keys
bool get isComplete => (_publicKey != null && _privateKey != null);
/// Creates a key pair from public and private keys.
KeyPair.from(PublicKey publicKey, PrivateKey privateKey, {String algorithm}) {
_publicKey = publicKey;
_privateKey = privateKey;
_algo = algorithm;
}
/// Creates a key pair from a specific size.
static Future<KeyPair> generate(KeySpec keySpec) async {
List<String> _supportedAlgorithms = ["RSA"];
if (!_supportedAlgorithms.contains(keySpec.algorithm)) {
throw KeyException(keySpec.algorithm + " not supported!");
}
if (keySpec.algorithm == "RSA") {
RSAKeySpec spec = keySpec;
try {
List<Uint8List> kp = await Platform().rsaKeypairGen(spec.size);
PublicKey _publicKey = PublicKey.fromBytes(kp.first, "RSA");
PrivateKey _privateKey = PrivateKey.fromBytes(kp.last, "RSA");
return KeyPair.from(_publicKey, _privateKey, algorithm: "RSA");
} on PlatformException catch (e) {
throw KeyException(e);
}
} else {
throw NotImplementedException("KeyPair generation not yet implemented.");
}
}
}
/// This represents a public key
class PublicKey extends Key {
/// Creates a public key from raw byte array
PublicKey.fromBytes(Uint8List bytes, String algorithm)
: super(bytes: bytes, algorithm: algorithm);
}
/// This represents a private key
class PrivateKey extends Key {
/// Creates a private key from raw byte array
PrivateKey.fromBytes(Uint8List bytes, String algorithm)
: super(bytes: bytes, algorithm: algorithm);
}

View File

@ -1,81 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'package:native_crypto/src/digest.dart';
import 'package:native_crypto/src/exceptions.dart';
import 'package:native_crypto/src/key.dart';
import 'platform.dart';
enum KdfAlgorithm { PBKDF2, SCrypt }
/// Represents a Key Derivation Function
abstract class KeyDerivation {
/// Returns the standard algorithm name for this key derivation function
KdfAlgorithm get algorithm;
/// Returns the derivated key
SecretKey get key;
/// Derive key
Future<void> derive();
}
class PBKDF2 implements KeyDerivation {
SecretKey _sk;
int _length;
int _iteration;
HashAlgorithm _hash;
@override
KdfAlgorithm get algorithm => KdfAlgorithm.PBKDF2;
@override
SecretKey get key => _sk;
PBKDF2({
int keyLength: 32,
int iteration: 10000,
HashAlgorithm hash: HashAlgorithm.SHA256,
}) {
_length = keyLength;
_iteration = iteration;
_hash = hash;
}
@override
Future<void> derive({String password, String salt}) async {
if (password == null || salt == null) {
throw KeyDerivationException("Password or Salt can't be null!");
}
if (_hash == null) {
throw KeyDerivationException("Hash Algorithm can't be null!");
}
Uint8List derivation = await Platform().pbkdf2(
password,
salt,
keyLength: _length,
iteration: _iteration,
algorithm: _hash,
);
_sk = SecretKey.fromBytes(derivation);
}
}
class Scrypt implements KeyDerivation {
@override
KdfAlgorithm get algorithm => KdfAlgorithm.SCrypt;
@override
SecretKey get key => throw UnimplementedError();
@override
Future<void> derive() {
throw UnimplementedError();
}
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
/// This represents security parameters.
abstract class KeySpec {
/// Returns the standard algorithm name for this key
String get algorithm;
}
class RSAKeySpec implements KeySpec {
int _size;
String get algorithm => "RSA";
/// Returns the size of RSA keys
int get size => _size;
RSAKeySpec(int size) {
_size = size;
}
}

View File

@ -1,161 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'cipher.dart';
import 'digest.dart';
import 'exceptions.dart';
import 'utils.dart';
/// Represents a platform, and is usefull to calling
/// methods from a specific platform.
class Platform {
/// Contains the channel for platform specific code.
static const MethodChannel _channel = const MethodChannel('native.crypto');
/// Calls native code.
static Future<T> call<T>(String method, [Map<String, dynamic> arguments]) {
return _channel.invokeMethod(method, arguments);
}
/// Calls native code that return list.
static Future<List<T>> callList<T>(String method,
[Map<String, dynamic> arguments]) {
return _channel.invokeListMethod(method, arguments);
}
/// Digests a message with a specific algorithm
///
/// Takes message and algorithm as parameters.
///
/// Returns a hash as [Uint8List].
Future<Uint8List> digest(
Uint8List message,
HashAlgorithm algorithm,
) async {
try {
final Uint8List hash = await call('digest', <String, dynamic>{
'message': message,
'algorithm': algorithm.name,
});
return hash;
} on PlatformException catch (e) {
throw DigestException(e.code + " : " + e.message);
}
}
/// Calls native PBKDF2.
///
/// Takes password and salt as parameters.
/// And optionnally keyLength in bytes, number of iterations and hash algorithm.
///
/// Returns a key as [Uint8List].
Future<Uint8List> pbkdf2(
String password,
String salt, {
int keyLength: 32,
int iteration: 10000,
HashAlgorithm algorithm: HashAlgorithm.SHA256,
}) async {
try {
final Uint8List key = await call('pbkdf2', <String, dynamic>{
'password': password,
'salt': salt,
'keyLength': keyLength,
'iteration': iteration,
'algorithm': algorithm.name,
});
return key;
} on PlatformException catch (e) {
throw KeyDerivationException(e.code + " : " + e.message);
}
}
/// Generates a random key.
///
/// Takes size in bits.
///
/// Returns a key as [Uint8List].
Future<Uint8List> keygen(int size) async {
try {
final Uint8List key = await call('keygen', <String, dynamic>{
'size': size,
});
return key;
} on PlatformException catch (e) {
throw KeyGenerationException(e.code + " : " + e.message);
}
}
/// Generates an RSA key pair.
///
/// Takes size in bits.
///
/// Returns a key pair as list of [Uint8List], the public key is the
/// first element, and the private is the last.
Future<List<Uint8List>> rsaKeypairGen(int size) async {
try {
final List<Uint8List> keypair =
await callList('rsaKeypairGen', <String, dynamic>{
'size': size,
});
return keypair;
} on PlatformException catch (e) {
throw KeyPairGenerationException(e.code + " : " + e.message);
}
}
/// Encrypts data with a secret key and algorithm.
///
/// Takes data, key, algorithm, mode and padding as parameters.
///
/// Encrypts data and returns cipher text
/// and IV as a list of [Uint8List].
Future<List<Uint8List>> encrypt(
Uint8List data,
Uint8List key,
CipherAlgorithm algorithm,
CipherParameters parameters,
) async {
try {
final List<Uint8List> payload =
await callList('encrypt', <String, dynamic>{
'data': data,
'key': key,
'algorithm': algorithm.name,
'mode': parameters.mode.name,
'padding': parameters.padding.name,
});
return payload;
} on PlatformException catch (e) {
throw EncryptionException(e.code + " : " + e.message);
}
}
/// Decrypts a payload with a secret key and algorithm.
///
/// The payload must be a list of `Uint8List`
/// with encrypted cipher as first and IV as second member.
Future<Uint8List> decrypt(
List<Uint8List> payload,
Uint8List key,
CipherAlgorithm algorithm,
CipherParameters parameters,
) async {
try {
final Uint8List data = await call('decrypt', <String, dynamic>{
'payload': payload,
'key': key,
'algorithm': algorithm.name,
'mode': parameters.mode.name,
'padding': parameters.padding.name,
});
return data;
} on PlatformException catch (e) {
throw DecryptionException(e.code + " : " + e.message);
}
}
}

View File

@ -1,215 +0,0 @@
// Copyright (c) 2021
// Author: Hugo Pointcheval
import 'dart:typed_data';
import 'package:native_crypto/native_crypto.dart';
import '../cipher.dart';
import '../exceptions.dart';
import '../key.dart';
import '../platform.dart';
import '../utils.dart';
/// Defines all available key sizes.
enum AESKeySize { bits128, bits192, bits256 }
extension AESKeySizeExtension on AESKeySize {
int get length {
Map<AESKeySize, int> table = <AESKeySize, int>{
AESKeySize.bits128: 128,
AESKeySize.bits192: 192,
AESKeySize.bits256: 256,
};
return table[this];
}
}
class AESCipher implements Cipher {
SecretKey _sk;
CipherParameters _params;
bool _isInit;
@override
CipherAlgorithm get algorithm => CipherAlgorithm.AES;
@override
SecretKey get secretKey => _sk;
@override
CipherParameters get parameters => _params;
@override
bool get isInitialized => _isInit;
@override
List<CipherParameters> get supportedParameters => [
CipherParameters(BlockCipherMode.CBC, PlainTextPadding.PKCS5),
];
/// Creates an AES cipher with specified secretKey and mode/padding
AESCipher(SecretKey secretKey, CipherParameters parameters) {
if (secretKey.algorithm != CipherAlgorithm.AES.name) {
List<int> _supportedSizes = [128, 192, 256];
if (!_supportedSizes.contains(secretKey.encoded.length)) {
throw CipherInitException("Invalid key length!");
}
} else if (!supportedParameters.contains(parameters)) {
throw CipherInitException("Invalid cipher parameters.");
}
_sk = secretKey;
_params = parameters;
_isInit = true;
}
/// Generates a secret key of specified size, then creates an AES cipher.
static Future<AESCipher> generate(
AESKeySize size, CipherParameters parameters) async {
SecretKey _sk = await SecretKey.generate(size.length, CipherAlgorithm.AES);
return AESCipher(_sk, parameters);
}
@override
Future<CipherText> encrypt(Uint8List data) async {
if (!_isInit) {
throw CipherInitException('Cipher not properly initialized.');
} else if (_sk == null || _sk.isEmpty) {
throw CipherInitException('Invalid key size.');
}
Uint8List dataToEncrypt;
int maxSize = 33554432;
AESCipherText cipherText = AESCipherText.empty();
// If data is bigger than 32mB -> split in chunks
if (data.length > maxSize) {
int chunkNb = (data.length / maxSize).ceil();
for (var i = 0; i < chunkNb; i++) {
if (i < (chunkNb - 1)) {
dataToEncrypt = data.sublist(i * maxSize, (i + 1) * maxSize);
} else {
dataToEncrypt = data.sublist(i * maxSize);
}
List<Uint8List> c = await Platform()
.encrypt(dataToEncrypt, _sk.encoded, algorithm, _params);
cipherText.append(c[0], c[1]);
}
} else {
List<Uint8List> c =
await Platform().encrypt(data, _sk.encoded, algorithm, _params);
cipherText.append(c[0], c[1]);
}
return cipherText;
}
@override
Future<Uint8List> decrypt(CipherText cipherText) async {
if (cipherText.algorithm != CipherAlgorithm.AES) {
throw DecryptionException("This cipher text's algorithm is not AES: " +
cipherText.algorithm.name +
"\nYou must use an AESCipherText.");
} else if (!_isInit) {
throw CipherInitException('Cipher not properly initialized.');
} else if (_sk == null || _sk.isEmpty) {
throw CipherInitException('Invalid key size.');
} else if (cipherText.bytes.length != cipherText.iv.length) {
throw DecryptionException(
"This cipher text's bytes chunks length is not the same as iv chunks length");
}
BytesBuilder decryptedData = BytesBuilder();
if (cipherText.size > 1) {
for (var i = 0; i < cipherText.size; i++) {
List<Uint8List> payload = [cipherText.bytes[i], cipherText.iv[i]];
Uint8List d =
await Platform().decrypt(payload, _sk.encoded, algorithm, _params);
decryptedData.add(d);
}
} else {
List<Uint8List> payload = [cipherText.bytes[0], cipherText.iv[0]];
Uint8List d =
await Platform().decrypt(payload, _sk.encoded, algorithm, _params);
decryptedData.add(d);
}
return decryptedData.toBytes();
}
}
class AESCipherText implements CipherText {
List<Uint8List> _bytes;
List<Uint8List> _iv;
@override
CipherAlgorithm get algorithm => CipherAlgorithm.AES;
@override
List<Uint8List> get bytes => _bytes;
@override
List<Uint8List> get iv => _iv;
@override
int get size => _bytes.length;
AESCipherText(Uint8List bytes, Uint8List iv) {
_bytes = List.from([bytes]);
_iv = List.from([iv]);
}
AESCipherText.from(List<Uint8List> bytes, List<Uint8List> iv) {
_bytes = bytes;
_iv = iv;
}
AESCipherText.empty() {
_bytes = <Uint8List>[];
_iv = <Uint8List>[];
}
void append(Uint8List bytes, Uint8List iv) {
_bytes.add(bytes);
_iv.add(iv);
}
/// Returns this ciphertext in [Uint8List] format.
///
/// Encoding
/// --------
/// Uint8List encoding is : IV_1 + M_1 + IV_2 + M_2 + ... + IV_n + M_n
///
/// Where **IV_k** is the IV of the cipher text **M_k**
///
/// IV is **always** 16 bytes long, And the **M** are all max
/// size (of 33 554 480 bytes) except the last one which is shorter than the others.
Uint8List encode() {
BytesBuilder builder = BytesBuilder();
for (var i = 0; i < size; i++) {
builder.add(_iv[i]);
builder.add(_bytes[i]);
}
return builder.toBytes();
}
/// Transforms a [Uint8List] to a *NativeCrypto* cipherText.
///
/// Decoding
/// --------
/// See the list as a chain of chunks (IV and Messages)
/// `[IV][MESSAGE][IV][MESSAGE] ... [IV][MESSA...]`
///
/// Chunk length is IV length + Message length = 16 + 33 554 480 bytes
void decode(Uint8List src) {
ByteBuffer buffer = src.buffer;
int chunkSize = 16 + 33554480;
int chunkNb = (buffer.lengthInBytes / chunkSize).ceil();
for (var i = 0; i < chunkNb; i++) {
_iv.add(buffer.asUint8List(i * chunkSize, 16));
if (i < (chunkNb - 1)) {
_bytes.add(buffer.asUint8List(16 + i * chunkSize, 33554480));
} else {
_bytes.add(buffer.asUint8List(16 + i * chunkSize));
}
}
}
}

View File

@ -1,141 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:flutter/foundation.dart';
import 'cipher.dart';
import 'digest.dart';
import 'exceptions.dart';
import 'kem.dart';
import 'keyderivation.dart';
extension HashAlgorithmExtension on HashAlgorithm {
String get name => describeEnum(this).toLowerCase();
}
extension KdfAlgorithmExtension on KdfAlgorithm {
String get name => describeEnum(this).toLowerCase();
}
extension CipherAlgorithmExtension on CipherAlgorithm {
String get name => describeEnum(this).toLowerCase();
}
extension KemAlgorithmExtension on KemAlgorithm {
String get name => describeEnum(this).toLowerCase();
}
extension BlockCipherModeExtension on BlockCipherMode {
String get name => describeEnum(this).toLowerCase();
}
extension PlainTextPaddingExtension on PlainTextPadding {
String get name => describeEnum(this).toLowerCase();
}
class Utils {
/// Returns [HashAlgorithm] from his name.
static HashAlgorithm getHashAlgorithm(String algorithm) {
String _query = algorithm.toLowerCase();
for (HashAlgorithm h in HashAlgorithm.values) {
if (_query == h.name) {
return h;
}
}
throw UtilsException("Unknown hash algorithm!");
}
/// Returns all available [HashAlgorithm] as String list
static List<String> getAvailableHashAlgorithms() {
List<String> _res = [];
for (HashAlgorithm h in HashAlgorithm.values) {
_res.add(h.name);
}
return _res;
}
/// Returns [KdfAlgorithm] from his name.
static KdfAlgorithm getKdfAlgorithm(String algorithm) {
String _query = algorithm.toLowerCase();
for (KdfAlgorithm h in KdfAlgorithm.values) {
if (_query == h.name) {
return h;
}
}
throw UtilsException("Unknown key derivation algorithm!");
}
/// Returns all available [KdfAlgorithm] as String list
static List<String> getAvailableKdfAlgorithms() {
List<String> _res = [];
for (KdfAlgorithm h in KdfAlgorithm.values) {
_res.add(h.name);
}
return _res;
}
/// Returns [CipherAlgorithm] from his name.
static CipherAlgorithm getCipherAlgorithm(String algorithm) {
String _query = algorithm.toLowerCase();
for (CipherAlgorithm c in CipherAlgorithm.values) {
if (_query == c.name) {
return c;
}
}
throw UtilsException("Unknown cipher algorithm!");
}
/// Returns all available [CipherAlgorithm] as String list
static List<String> getAvailableCipherAlgorithms() {
List<String> _res = [];
for (CipherAlgorithm c in CipherAlgorithm.values) {
_res.add(c.name);
}
return _res;
}
/// Returns [KemAlgorithm] from his name.
static KemAlgorithm getKemAlgorithm(String algorithm) {
String _query = algorithm.toLowerCase();
for (KemAlgorithm k in KemAlgorithm.values) {
if (_query == k.name) {
return k;
}
}
throw UtilsException("Unknown KEM algorithm!");
}
/// Returns all available [KemAlgorithm] as String list
static List<String> getAvailableKemAlgorithms() {
List<String> _res = [];
for (KemAlgorithm k in KemAlgorithm.values) {
_res.add(k.name);
}
return _res;
}
/// Returns [CipherParameters] from string.
///
/// For example, `CBC/PKCS5` gives a CipherParameters with
/// CBC mode and PKCS5 as padding.
static CipherParameters getCipherParameters(String parameters) {
List<String> _query = parameters.toLowerCase().split("/");
BlockCipherMode _mode;
PlainTextPadding _padding;
for (BlockCipherMode b in BlockCipherMode.values) {
if (_query[0] == b.name) {
_mode = b;
}
}
for (PlainTextPadding p in PlainTextPadding.values) {
if (_query[1] == p.name) {
_padding = p;
}
}
if (_mode == null || _padding == null) {
throw UtilsException("Unknown parameters!");
} else {
return CipherParameters(_mode, _padding);
}
}
}

66
melos.yaml Normal file
View File

@ -0,0 +1,66 @@
name: NativeCrypto
packages:
- packages/**
command:
bootstrap:
usePubspecOverrides: true
version:
updateGitTagRefs: true
linkToCommits: false # Gitea not yet supported
workspaceChangelog: true
branch: master
scripts:
lint:all:
run: melos run analyze && melos run format
description: Run all static analysis checks.
analyze:
run: |
melos exec -c 1 -- 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`.
test:selective_unit_test:
run: melos exec -- flutter test --no-pub --coverage
description: Run Flutter tests for a specific package in this project.
select-package:
dir-exists:
- test
ignore:
- '*example*'
test:all:
run: melos run test:selective_unit_test --no-select
description: Run all Flutter tests in this project.
quality-check:
run: |
melos clean && \
melos bootstrap && \
melos run test:all
description: Run all targets generally expected in CI for a full local quality check.
publish:validate:
run: melos publish --diff="origin/$DRONE_COMMIT_BRANCH...HEAD" --yes
# publish:
# run: melos publish --diff="origin/$DRONE_COMMIT_BRANCH...HEAD" --no-dry-run --yes
# Additional cleanup lifecycle script, executed when `melos clean` is run.
postclean: >
melos exec -c 6 -- "flutter clean"

View File

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3
revision: cf4400006550b70f28e4b4af815151d1e74846c6
channel: stable
project_type: plugin

View File

@ -0,0 +1,63 @@
## 0.2.0
> Note: This release has breaking changes.
- **REFACTOR**: (WIP) optimize exceptions and bytearray.
- **REFACTOR**: update benchmark page.
- **FIX**: accept empty decrypted plaintext.
- **FIX**: update code to pass tests.
- **FIX**: update code to pass all tests.
- **FIX**: change tag length in aes gcm cipher.
- **FEAT**: update example for ios file encryption.
- **FEAT**: update example with benchmark + file encryption.
- **FEAT**: update example.
- **FEAT**: rework bytearray and memory optimization, simplify API.
- **DOCS**: update readmes/licences.
- **DOCS**: update readme.
- **BREAKING** **FEAT**: rework full api with better object oriented architecture.
## 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.

View File

@ -0,0 +1,23 @@
NativeCrypto
MIT License
Copyright (c) 2019 - 2023 Hugo Pointcheval
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
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.

View File

@ -0,0 +1,3 @@
# NativeCrypto
Readme available at [project root](../../README.md).

View File

@ -0,0 +1 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml

View File

@ -22,6 +22,7 @@
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
@ -33,5 +34,13 @@
# Web related
lib/generated_plugin_registrant.dart
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# 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

View File

@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: cd41fdd495f6944ecd3506c21e94c6567b073278
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
- platform: web
create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -0,0 +1,15 @@
## 0.0.1
- **REFACTOR**: update benchmark page.
- **REFACTOR**: change file organization.
- **PERF**: x10 perfomance improvement on android with better list management.
- **FIX**: update code to pass all tests.
- **FIX**: benchmark output.
- **FIX**: update and fix code.
- **FEAT**: update example for ios file encryption.
- **FEAT**: update example with benchmark + file encryption.
- **FEAT**: update example.
- **FEAT**: rework bytearray and memory optimization, simplify API.
- **FEAT**: export new exceptions.
- **FEAT**: add PointyCastle benchmark.

View File

@ -0,0 +1 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml

View File

@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@ -26,24 +26,28 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "fr.pointcheval.native_crypto_example"
minSdkVersion 16
targetSdkVersion 28
minSdkVersion 26
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -53,6 +57,7 @@ android {
signingConfig signingConfigs.debug
}
}
namespace 'fr.pointcheval.native_crypto_example'
}
flutter {
@ -61,7 +66,4 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.pointcheval.native_crypto_example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,21 +1,24 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.pointcheval.native_crypto_example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name="io.flutter.app.FlutterApplication"
android:label="native_crypto_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

View File

@ -0,0 +1,6 @@
package fr.pointcheval.native_crypto_example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.pointcheval.native_crypto_example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = "1.6.21"
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -14,7 +14,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View File

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View File

@ -1,6 +1,6 @@
#Fri Dec 18 22:37:36 CET 2020
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

View File

@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

View File

@ -1,3 +1,4 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
@ -18,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>11.0</string>
</dict>
</plist>

View File

@ -1,3 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -1,3 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
platform :ios, '13.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

View File

@ -0,0 +1,75 @@
PODS:
- DKImagePickerController/Core (4.3.4):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.4)
- DKImagePickerController/PhotoGallery (4.3.4):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.4)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
- DKPhotoGallery/Preview (= 0.0.17)
- DKPhotoGallery/Resource (= 0.0.17)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.17):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- native_crypto_ios (0.0.1):
- Flutter
- SDWebImage (5.15.5):
- SDWebImage/Core (= 5.15.5)
- SDWebImage/Core (5.15.5)
- SwiftyGif (5.4.4)
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- SDWebImage
- SwiftyGif
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
native_crypto_ios:
:path: ".symlinks/plugins/native_crypto_ios/ios"
SPEC CHECKSUMS:
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d
SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
COCOAPODS: 1.11.3

View File

@ -3,17 +3,17 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 54;
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 */; };
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 */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
D294241BEFD2D3EF500C0577 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -32,8 +32,10 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2DBF05A146610778D425B9D9 /* 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 = "<group>"; };
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 = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@ -44,9 +46,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D3EC88B33A6F35C67BAC8349 /* 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 = "<group>"; };
FB0023B75BB4BD74C9F8D280 /* 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 = "<group>"; };
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,22 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D294241BEFD2D3EF500C0577 /* Pods_Runner.framework in Frameworks */,
57C8B66CEF3FCADD27359CF2 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
8C27A9C4EB5E71366C317BF8 /* Frameworks */ = {
56AFD4323C9A594E66DE3CA2 /* Pods */ = {
isa = PBXGroup;
children = (
C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */,
4276EDF2B0F07350DCE3D5A1 /* Pods-Runner.debug.xcconfig */,
2ABDB9EE0C984D50A4E8A819 /* Pods-Runner.release.xcconfig */,
5CD4F461EBFD40A270B90468 /* Pods-Runner.profile.xcconfig */,
);
name = Frameworks;
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
@ -86,8 +89,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
9F5A6E791DB57C1B2E0BF33E /* Pods */,
8C27A9C4EB5E71366C317BF8 /* Frameworks */,
56AFD4323C9A594E66DE3CA2 /* Pods */,
CECC25F8D34DF7912A93B0E6 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -106,7 +109,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@ -115,21 +117,12 @@
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
CECC25F8D34DF7912A93B0E6 /* Frameworks */ = {
isa = PBXGroup;
children = (
9B565CC9BF59F330E881E6A5 /* Pods_Runner.framework */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9F5A6E791DB57C1B2E0BF33E /* Pods */ = {
isa = PBXGroup;
children = (
FB0023B75BB4BD74C9F8D280 /* Pods-Runner.debug.xcconfig */,
2DBF05A146610778D425B9D9 /* Pods-Runner.release.xcconfig */,
D3EC88B33A6F35C67BAC8349 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
@ -139,14 +132,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
D72ACB84F79B4AD23991D136 /* [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 */,
FF811A27CCC4B4D5EBD756CE /* [CP] Embed Pods Frameworks */,
D9E654BBB4893905628A201A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -163,18 +156,17 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Chromium Authors";
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = 6Z5P8GG96U;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@ -208,6 +200,7 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -222,6 +215,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -234,7 +228,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
D72ACB84F79B4AD23991D136 /* [CP] Check Pods Manifest.lock */ = {
C54B0935BA386F7769969821 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -256,15 +250,17 @@
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;
};
FF811A27CCC4B4D5EBD756CE /* [CP] Embed Pods Frameworks */ = {
D9E654BBB4893905628A201A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@ -346,7 +342,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -364,18 +360,12 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoFlutterExample;
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -430,7 +420,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -479,11 +469,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@ -498,18 +489,12 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoFlutterExample;
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -527,18 +512,12 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoFlutterExample;
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@ -38,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -61,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

Some files were not shown because too many files have changed in this diff Show More