Compare commits

..

No commits in common. "master" and "v0.0.3" have entirely different histories.

294 changed files with 1821 additions and 13268 deletions

View File

@ -1,20 +0,0 @@
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

442
.gitignore vendored
View File

@ -1,7 +1,107 @@
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,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
# 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/
### Dart ###
# See https://www.dartlang.org/guides/libraries/private-files
@ -9,7 +109,6 @@
# 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
@ -17,9 +116,6 @@ 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.
@ -29,25 +125,13 @@ 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/
.fvm/
.flutter-plugins
.flutter-plugins-dependencies
.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
@ -55,7 +139,6 @@ lib/generated_plugin_registrant.dart
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/key.properties
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
@ -76,15 +159,12 @@ lib/generated_plugin_registrant.dart
**/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.*
@ -95,34 +175,8 @@ lib/generated_plugin_registrant.dart
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
### 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
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
@ -132,9 +186,6 @@ replay_pid*
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
@ -155,9 +206,6 @@ replay_pid*
# 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
@ -174,7 +222,6 @@ cmake-build-*/
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
@ -185,9 +232,6 @@ 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
@ -200,14 +244,20 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains+all Patch ###
# Ignore everything but code style settings and run configurations
# that are supposed to be shared within teams.
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/*
.idea/
!.idea/codeStyles
!.idea/runConfigurations
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Kotlin ###
# Compiled class file
@ -215,27 +265,22 @@ fabric.properties
# 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
### 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*
hs_err_pid*
### macOS ###
# General
@ -246,7 +291,6 @@ fabric.properties
# Icon must end with two \r
Icon
# Thumbnails
._*
@ -266,25 +310,15 @@ Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Objective-C ###
### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## 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)
## Build generated
DerivedData/
*.moved-aside
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
@ -293,73 +327,19 @@ DerivedData/
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
# Pods/
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# fastlane
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
### Objective-C Patch ###
### Rust ###
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
### Swift ###
# Xcode
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Playgrounds
timeline.xctimeline
playground.xcworkspace
@ -369,12 +349,9 @@ playground.xcworkspace
# 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/
# 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
@ -388,47 +365,37 @@ 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/
# 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/screenshots/**/*.png
# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
### 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
@ -457,26 +424,121 @@ $RECYCLE.BIN/
*.lnk
### Xcode ###
# Xcode
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Xcode 8 and earlier
## User settings
### Xcode Patch ###
## 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
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
### Xcode Patch ###
**/xcshareddata/WorkspaceSettings.xcsettings
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,java,jetbrains+all,kotlin,linux,objective-c,rust,swift,windows,xcode
### 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
# 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

View File

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

View File

@ -1,7 +0,0 @@
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
View File

@ -1,28 +0,0 @@
{
// 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
View File

@ -1,19 +0,0 @@
{
"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.",
]
}
],
}

View File

@ -1,6 +0,0 @@
# 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,157 +1,3 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2023-04-05
### Changes
---
Packages with breaking changes:
- [`native_crypto` - `v0.2.0`](#native_crypto---v020)
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.
## 0.0.1
* TODO: Describe initial release.

View File

@ -1,6 +1,8 @@
native_crypto
MIT License
Copyright (c) 2020-2023 Hugo Pointcheval
Copyright (c) 2020 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

318
README.md
View File

@ -1,280 +1,142 @@
<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>
# NativeCrypto
<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>
Fast crypto functions for Flutter.
<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>
* Table of content
* [Background](#background)
* [Performances](#performances)
* [Installation](#installation)
* [Usage](#usage)
* [Example](#example)
* [How](#how)
* [Todo](#todo)
<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>
## Background
---
🤔 Why I started this project ?
Because I faced a performance issue when I was using **PointyCastle**.
[[Changelog]](./CHANGELOG.md) | [[License]](./LICENSE)
It's quite simple, judge for yourself, these are times for AES256 encryption on an Android device (**Huawei P30 Pro**).
---
| Size | PointyCastle |
|------|--------------|
| 100 kB | 190 ms
| 200 kB | 314 ms
| 300 kB | 1138 ms
| 400 kB | 2781 ms
| 500 kB | 4691 ms
| 600 kB | 7225 ms
| 700 kB | 10264 ms
| 800 kB | 13582 ms
| 900 kB | 17607 ms
## About
> We notice that these times, in addition to being far too big, are not even linear.
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/)
## Performances
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:
⏱ On an **Android 10** device: **Huawei P30 Pro**
![](resources/benchmarks.png)
| Size | NativeCrypto |
|------|--------------|
| 1 mB | 27 ms
| 2 mB | 43 ms
| 3 mB | 78 ms
| 4 mB | 93 ms
| 5 mB | 100 ms
| 10 mB | 229 ms
| 50 mB | 779 ms
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.
## Installation
In short, NativeCrypto is incomparable with PointyCastle.
🚧 You can easely setup a Flutter project with this plugin.
## Features
Just add these lines in your **pubspec.yaml**:
* Hash functions
- SHA-256
- SHA-384
- SHA-512
* HMAC functions
- HMAC-SHA-256
- HMAC-SHA-384
- HMAC-SHA-512
* Secure random
* PBKDF2
* AES
- Uint8List encryption/decryption
- File encryption/decryption
## Quick start
```dart
import 'package:native_crypto/native_crypto.dart';
Future<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());
}
```yaml
native_crypto:
git:
url: https://gogs.pointcheval.fr/hugo/native-crypto-flutter.git
ref: v0.0.x
```
Check the [example](./native_crypto/example) for a complete example.
> Replace "x" with the current version!
Please take a look a the compatibility table below to check if your target is supported.
Then in your code:
> Note: This **Flutter** example must run on a real device or a simulator.
```dart
// Symmetric crypto.
import 'package:native_crypto/symmetric_crypto.dart';
// To handle exceptions.
import 'package:native_crypto/exceptions.dart';
```
## Usage
#### Compatibility
First, check compatibility with your targets.
| iOS | Android | MacOS | Linux | Windows | Web |
| --- | ------- | ----- | ----- | ------- | --- |
| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md))
NativeCrypto ciphertexts are formatted as follow:
```
+------------------+--------------------+------------------+
| Nonce (12 bytes) | Cipher text (n-28) | Tag (16 bytes) |
+------------------+--------------------+------------------+
```
> Warning: If your data comes from another source, make sur to use the same format.
#### Hash
To digest a message, you'll need to initialize a Hasher object implementing `Hash` . Then, you can digest your message.
To create an AES instance, and generate a key.
```dart
Hash hasher = Sha256();
Uint8List digest = await hasher.digest(message);
AES aes = AES();
await aes.init(KeySize.bits256)
```
> 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.
You can also generate key, then use it in AES.
```dart
Hmac hmac = HmacSha256();
Uint8List hmac = await hmac.digest(message, secretKey);
Uint8List aeskey = await KeyGenerator().secretKey(keySize: KeySize.bits256);
AES aes = AES(key: aeskey);
```
> 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.
You can create a key with PBKDF2.
```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
Uint8List key = await KeyGenerator().pbkdf2(password, salt, keyLength: 32, iteration: 10000);
AES aes = AES(key: key);
```
#### Key derivation
You can derive a `SecretKey` using **PBKDF2**.
First, you need to initialize a `Pbkdf2` object.
Then you can encrypt/decrypt data with this instance.
```dart
final Pbkdf2 pbkdf2 = Pbkdf2(
length: 32, // 32 bytes
iterations: 1000,
salt: salt.toBytes(),
hashAlgorithm: HashAlgorithm.sha256,
);
encryptedPayload = await aes.encrypt(data);
decryptedPayload = await aes.decrypt(encryptedPayload);
```
Then, you can derive a `SecretKey` from a password.
Or you can also use AES on the fly with different keys.
```dart
SecretKey secretKey = await pbkdf2(password: password);
Uint8List aeskey = await KeyGenerator().secretKey(keySize: KeySize.bits256);
encryptedPayload = await AES().encrypt(data, key: aeskey);
decryptedPayload = await AES().decrypt(encryptedPayload, key: aeskey);
```
> Note: Pbkdf2 is a callable class. You can use it like a function.
## Example
#### Cipher
🔍 Look in **example/lib/** for an example app.
And now, you can use the `SecretKey` to encrypt/decrypt a message.
## How
First, you need to initialize a `Cipher` object.
🔬 But how it is possible ??
```dart
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
```
Using the native implementation of crypto libs available on each OS.
Then, you can encrypt your message.
For **Android**:
```dart
final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);
```
* [javax.crypto](https://docs.oracle.com/javase/7/docs/api/javax/crypto/package-summary.html)
* [java.security](https://docs.oracle.com/javase/7/docs/api/java/security/package-summary.html)
After an encryption you obtain a `CipherText` which contains chunks. You can get the underlying bytes with `cipherText.bytes` .
For **iOS**:
Uppon receiving encrypted message `receivedData` , you can decrypt it.
You have to reconstruct the ciphertext and the setup the chunk factory.
* [CommonCrypto](https://developer.apple.com/library/archive/documentation/Security/Conceptual/cryptoservices/Introduction/Introduction.html)
```dart
final CipherText<AESCipherChunk> receivedCipherText CipherText(
receivedData,
chunkFactory: (bytes) => AESCipherChunk(
bytes,
ivLength: cipher.mode.ivLength,
tagLength: cipher.mode.tagLength,
),
),
```
## Todos
Then, you can decrypt your message.
🚀 You can contribute to this project.
```dart
Uint8List message = await cipher.decrypt(receivedCipherText);
```
#### Files
You can encrypt/decrypt files.
First, you need to initialize a `Cipher` object.
```dart
final AES cipher = AES(
key: key,
mode: AESMode.gcm,
padding: AESPadding.none,
);
```
Then, you can encrypt your file.
```dart
await cipher.encryptFile(plainText, cipherText);
```
> Note: `plainText` and `cipherText` are `File` objects.
You can decrypt your file.
```dart
await cipher.decryptFile(cipherText, plainText);
```
#### Advanced
You can force the use of a specific IV. Please note that the IV must be unique for each encryption.
```dart
final CipherText<AESCipherChunk> cipherText = await cipher.encryptWithIV(message, iv);
```
⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing.
## Development
### Android
> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2b-add-android-platform-code-ktjava
* Launch Android Studio.
* Select Open an existing Android Studio Project in the Welcome to Android Studio dialog, or select File > Open from the menu, and select the `packages/native_crypto/example/android/build.gradle` file.
* In the Gradle Sync dialog, select OK.
* In the Android Gradle Plugin Update dialog, select 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.
* [x] Implement working cross platform AES encryption/decryption.
* [x] Different key sizes support.
* [x] Improve performances.
* [x] Add exceptions.
* [x] PBKDF2 support.
* [ ] Add other ciphers.
* [ ] Clean platform specific code.
* [ ] Add asym crypto support...

View File

@ -1,15 +1,15 @@
group 'fr.pointcheval.native_crypto_android'
group 'fr.pointcheval.native_crypto'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.6.21'
ext.kotlin_version = '1.3.50'
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -17,7 +17,7 @@ buildscript {
rootProject.allprojects {
repositories {
google()
mavenCentral()
jcenter()
}
}
@ -25,27 +25,20 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 30
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 26
minSdkVersion 16
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.documentfile:documentfile:1.0.1'
}

View File

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

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip

1
android/settings.gradle Normal file
View File

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

View File

@ -1,3 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.pointcheval.native_crypto_android">
package="fr.pointcheval.native_crypto">
</manifest>

View File

@ -0,0 +1,166 @@
/*
* 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
import java.security.MessageDigest
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
/** NativeCryptoPlugin */
public class NativeCryptoPlugin : FlutterPlugin, MethodCallHandler {
// CRYPTO CONSTS
private val HASH_FUNC = "SHA-256"
private val SYM_CRYPTO_METHOD = "AES"
private val SYM_CRYPTO_PADDING = "AES/CBC/PKCS5PADDING"
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "native.crypto.helper")
channel.setMethodCallHandler(NativeCryptoPlugin());
}
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "native.crypto.helper")
channel.setMethodCallHandler(NativeCryptoPlugin())
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "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 key = pbkdf2(password!!, salt!!, keyLength!!, iteration!!)
if (key.isNotEmpty()) {
result.success(key)
} else {
result.error("PBKDF2ERROR", "PBKDF2 KEY IS NULL.", null)
}
} else if (call.method == "symKeygen") {
val keySize = call.argument<Int>("size") // 128, 192, 256
val aesKey = symKeygen(keySize!!) // Collection<ByteArray>
if (aesKey.isNotEmpty()) {
result.success(aesKey)
} else {
result.error("SYMKEYGENERROR", "GENERATED KEY IS NULL.", null)
}
} else if (call.method == "symEncrypt") {
val payload = call.argument<ByteArray>("payload") // ByteArray
val aesKey = call.argument<ByteArray>("aesKey") // ByteArray
val encryptedPayload = symEncrypt(payload!!, aesKey!!) // Collection<ByteArray>
if (encryptedPayload.isNotEmpty()) {
result.success(encryptedPayload)
} else {
result.error("ENCRYPTIONERROR", "ENCRYPTED PAYLOAD IS NULL.", null)
}
} else if (call.method == "symDecrypt") {
val payload = call.argument<Collection<ByteArray>>("payload") // Collection<ByteArray>
val aesKey = call.argument<ByteArray>("aesKey") // ByteArray
var decryptedPayload : ByteArray? = null
try {
decryptedPayload = symDecrypt(payload!!, aesKey!!)
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("DECRYPTIONERROR", "AN ERROR OCCURED WHILE DECRYPTING.", null)
}
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
}
// CRYPTO NATIVE FUNCTIONS
private fun digest(obj: ByteArray?): ByteArray {
val md = MessageDigest.getInstance(HASH_FUNC)
return md.digest(obj)
}
private fun pbkdf2(password : String, salt : String, keyLength : Int, iteration : Int) : ByteArray {
val chars: CharArray = password.toCharArray()
val spec = PBEKeySpec(chars, salt.toByteArray(), iteration, keyLength * 8)
val skf: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
return skf.generateSecret(spec).encoded
}
private fun symKeygen(keySize : Int): ByteArray {
val SYM_CRYPTO_BITS = keySize
val secureRandom = SecureRandom()
val keyGenerator = KeyGenerator.getInstance(SYM_CRYPTO_METHOD)
keyGenerator?.init(SYM_CRYPTO_BITS, secureRandom)
val skey = keyGenerator?.generateKey()
return skey!!.encoded
}
private fun symEncrypt(payload: ByteArray, aesKey: ByteArray): Collection<ByteArray> {
val mac = digest(aesKey + payload)
val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING)
cipher.init(Cipher.ENCRYPT_MODE, key)
val encryptedBytes = cipher.doFinal(mac + payload)
val iv = cipher.iv
return listOf(encryptedBytes, iv);
}
private fun symDecrypt(payload: Collection<ByteArray>, aesKey: ByteArray): ByteArray? {
val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING);
val iv = payload.last();
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
val decryptedBytes = cipher.doFinal(payload.first());
val mac = decryptedBytes.copyOfRange(0, 32)
val decryptedContent = decryptedBytes.copyOfRange(32, decryptedBytes.size)
val verificationMac = digest(aesKey + decryptedContent)
if (mac.contentEquals(verificationMac)) return decryptedContent
return null;
}
}

View File

@ -22,7 +22,6 @@
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
@ -34,13 +33,5 @@
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

View File

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

7
example/android/.gitignore vendored Normal file
View File

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

View File

@ -26,28 +26,24 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
compileSdkVersion 28
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 26
targetSdkVersion flutter.targetSdkVersion
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -57,7 +53,6 @@ android {
signingConfig signingConfigs.debug
}
}
namespace 'fr.pointcheval.native_crypto_example'
}
flutter {
@ -66,4 +61,7 @@ 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,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.pointcheval.native_crypto_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,24 +1,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
<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. -->
<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,12 @@
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

@ -0,0 +1,8 @@
<?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,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.pointcheval.native_crypto_example">
<!-- 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.6.21"
ext.kotlin_version = '1.3.50'
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -14,7 +14,7 @@ buildscript {
allprojects {
repositories {
google()
mavenCentral()
jcenter()
}
}

View File

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

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip

View File

@ -0,0 +1,15 @@
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,4 +1,3 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
@ -19,7 +18,6 @@ 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>en</string>
<string>$(DEVELOPMENT_LANGUAGE)</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>11.0</string>
<string>8.0</string>
</dict>
</plist>

View File

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

View File

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

90
example/ios/Podfile Normal file
View File

@ -0,0 +1,90 @@
# 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

22
example/ios/Podfile.lock Normal file
View File

@ -0,0 +1,22 @@
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.9.1

View File

@ -3,17 +3,21 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 46;
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 */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
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 */
@ -23,6 +27,8 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -32,21 +38,23 @@
/* 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>"; };
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>"; };
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>"; };
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>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; 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>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
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>"; };
9B565CC9BF59F330E881E6A5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -54,28 +62,29 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
57C8B66CEF3FCADD27359CF2 /* Pods_Runner.framework in Frameworks */,
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
D294241BEFD2D3EF500C0577 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
56AFD4323C9A594E66DE3CA2 /* Pods */ = {
8C27A9C4EB5E71366C317BF8 /* Frameworks */ = {
isa = PBXGroup;
children = (
4276EDF2B0F07350DCE3D5A1 /* Pods-Runner.debug.xcconfig */,
2ABDB9EE0C984D50A4E8A819 /* Pods-Runner.release.xcconfig */,
5CD4F461EBFD40A270B90468 /* Pods-Runner.profile.xcconfig */,
C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */,
);
name = Pods;
path = Pods;
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
@ -89,8 +98,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
56AFD4323C9A594E66DE3CA2 /* Pods */,
CECC25F8D34DF7912A93B0E6 /* Frameworks */,
9F5A6E791DB57C1B2E0BF33E /* Pods */,
8C27A9C4EB5E71366C317BF8 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -109,6 +118,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@ -117,12 +127,22 @@
path = Runner;
sourceTree = "<group>";
};
CECC25F8D34DF7912A93B0E6 /* Frameworks */ = {
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
9B565CC9BF59F330E881E6A5 /* Pods_Runner.framework */,
);
name = Frameworks;
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 */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
@ -132,14 +152,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
C54B0935BA386F7769969821 /* [CP] Check Pods Manifest.lock */,
D72ACB84F79B4AD23991D136 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
D9E654BBB4893905628A201A /* [CP] Embed Pods Frameworks */,
FF811A27CCC4B4D5EBD756CE /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -156,8 +176,8 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
@ -166,7 +186,7 @@
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@ -200,7 +220,6 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -211,11 +230,10 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -228,7 +246,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
C54B0935BA386F7769969821 /* [CP] Check Pods Manifest.lock */ = {
D72ACB84F79B4AD23991D136 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -250,17 +268,15 @@
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;
};
D9E654BBB4893905628A201A /* [CP] Embed Pods Frameworks */ = {
FF811A27CCC4B4D5EBD756CE /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@ -303,6 +319,7 @@
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -342,7 +359,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -358,12 +375,16 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -375,6 +396,7 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -420,7 +442,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -430,6 +452,7 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -469,12 +492,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@ -487,12 +509,16 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -510,12 +536,16 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

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

View File

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

View File

@ -4,8 +4,6 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Native Crypto</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -42,14 +40,6 @@
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UISupportsDocumentBrowser</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<false/>
</dict>
</plist>

View File

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

303
example/lib/main.dart Normal file
View File

@ -0,0 +1,303 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:developer';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:native_crypto/symmetric_crypto.dart';
import 'package:native_crypto/exceptions.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final textController = TextEditingController();
final pwdController = TextEditingController();
String _output = 'none';
String _bench;
AES aes = AES();
List<Uint8List> encryptedPayload;
Uint8List decryptedPayload;
Uint8List key;
void _generateKey() async {
// You can also generate key before creating aes object.
// Uint8List aeskey = await KeyGenerator().secretKey(keySize: KeySize.bits256);
// AES aes = AES(key: aeskey);
var output;
try {
await aes.init(KeySize.bits256);
output = 'Key generated. Length: ${aes.key.length}';
} catch (e) {
// PlatformException or KeyException, both have message property.
output = e.message;
}
setState(() {
_output = output;
});
}
void _pbkdf2() async {
final password = pwdController.text.trim();
var output;
if (password.isEmpty) {
output = 'Password is empty';
} else {
key = await KeyGenerator().pbkdf2(password, 'salt');
output = 'Key successfully derived.';
}
setState(() {
_output = output;
});
}
void _encrypt() async {
final plainText = textController.text.trim();
var output;
if (plainText.isEmpty) {
output = 'Entry is empty';
} else {
var stringToBytes = TypeHelper().stringToBytes(plainText);
// You can also pass a specific key.
// encryptedPayload = await AES().encrypt(stringToBytes, key: aeskey);
encryptedPayload = await aes.encrypt(stringToBytes, key: key?? null);
output = 'String successfully encrypted.';
}
setState(() {
_output = output;
});
}
void _alter() async {
var output;
if (encryptedPayload == null || encryptedPayload[0].isEmpty) {
output = 'Encrypt before altering payload!';
} else {
// Add 1 to the first byte
encryptedPayload[0][0] += 1;
output = 'Payload altered.';
}
setState(() {
_output = output;
});
}
void _decrypt() async {
var output;
if (encryptedPayload == null || encryptedPayload[0].isEmpty) {
output = 'Encrypt before decrypting!';
} else {
// You can also pass a specific key.
// decryptedPayload = await AES().decrypt(encryptedPayload, key: aeskey);
try {
decryptedPayload = await aes.decrypt(encryptedPayload, key: key?? null);
var bytesToString = TypeHelper().bytesToString(decryptedPayload);
output = 'String successfully decrypted:\n\n$bytesToString';
} on DecryptionException catch (e) {
output = e.message;
}
}
setState(() {
_output = output;
});
}
Future<String> _benchmark(int megabytes) async {
String output;
var bigFile = Uint8List(megabytes * 1000000);
var before = DateTime.now();
var encryptedBigFile = await aes.encrypt(bigFile);
var after = DateTime.now();
var benchmark =
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
output = '$megabytes MB\nAES Encryption: $benchmark ms\n';
before = DateTime.now();
await aes.decrypt(encryptedBigFile);
after = DateTime.now();
benchmark = after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
output += 'AES Decryption: $benchmark ms\n\n';
return output;
}
void _testPerf({int megabytes}) async {
var output = '';
if (megabytes != null) {
output = await _benchmark(megabytes);
setState(() {
_output = output;
});
} else {
setState(() {
_bench = 'Open the logcat!';
});
for (int i=1;i<=50;i+=10) {
var benchmark = await _benchmark(i);
log(benchmark, name: 'fr.pointcheval.native_crypto');
}
}
}
@override
void initState() {
// Generate AES instance on init.
_generateKey();
super.initState();
}
void dispose() {
// Clean up the controller when the widget is disposed.
textController.dispose();
pwdController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Native Crypto'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 10, 20),
child: Center(
child: Column(
children: <Widget>[
TextField(
controller: pwdController,
decoration: InputDecoration(
hintText: 'Test password',
),
),
SizedBox(height: 20),
FlatButton(
onPressed: _pbkdf2,
color: Colors.blue,
child: Text(
'Pbkdf2',
style: TextStyle(color: Colors.white),
)),
SizedBox(height: 30),
TextField(
controller: textController,
decoration: InputDecoration(
hintText: 'Text to encrypt.',
),
),
SizedBox(height: 20),
FlatButton(
onPressed: _encrypt,
color: Colors.blue,
child: Text(
'Encrypt String',
style: TextStyle(color: Colors.white),
)),
FlatButton(
onPressed: _alter,
color: Colors.blue,
child: Text(
'Alter encrypted payload',
style: TextStyle(color: Colors.white),
)),
FlatButton(
onPressed: _decrypt,
color: Colors.blue,
child: Text(
'Decrypt String',
style: TextStyle(color: Colors.white),
)),
SizedBox(height: 20),
// Output
Text(
_output,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
FlatButton(
onPressed: () {
_testPerf(megabytes: 1);
},
color: Colors.blue,
child: Text(
'Benchmark 1 MB',
style: TextStyle(color: Colors.white),
)),
FlatButton(
onPressed: () {
_testPerf(megabytes: 10);
},
color: Colors.blue,
child: Text(
'Benchmark 10 MB',
style: TextStyle(color: Colors.white),
)),
FlatButton(
onPressed: () {
_testPerf(megabytes: 50);
},
color: Colors.blue,
child: Text(
'Benchmark 50 MB',
style: TextStyle(color: Colors.white),
)),
SizedBox(height: 20),
FlatButton(
onPressed: () {
_testPerf();
},
color: Colors.blue,
child: Text(
'Full benchmark',
style: TextStyle(color: Colors.white),
)),
(_bench != null && _bench.isNotEmpty)
? Text(_bench)
: Container(),
],
),
),
),
),
),
);
}
}
/// Contains some useful functions.
class TypeHelper {
/// Returns bytes `Uint8List` from a `String`.
Uint8List stringToBytes(String source) {
var list = source.runes.toList();
var bytes = Uint8List.fromList(list);
return bytes;
}
/// Returns a `String` from bytes `Uint8List`.
String bytesToString(Uint8List bytes) {
var string = String.fromCharCodes(bytes);
return string;
}
}

View File

@ -1,51 +1,31 @@
name: native_crypto_example
description: Demonstrates how to use the native_crypto plugin.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev
publish_to: 'none'
environment:
sdk: ">=2.17.0 <3.0.0"
sdk: ">=2.1.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter: { sdk: flutter }
native_crypto: { path: ../ }
cupertino_icons: ^1.0.5
flutter_riverpod: ^2.1.3
pointycastle: ^3.6.2
flutter_bloc: ^8.1.1
flutter:
sdk: flutter
wyatt_architecture:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 0.1.0+1
wyatt_type_utils:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 0.0.4
wyatt_bloc_helper:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 2.0.0
get_it: ^7.2.0
file_picker: ^5.2.7
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test: { sdk: flutter }
flutter_test:
sdk: flutter
wyatt_analysis:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 2.4.1
native_crypto:
path: ../
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
@ -53,8 +33,8 @@ flutter:
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.

View File

@ -0,0 +1,27 @@
// 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

@ -34,5 +34,4 @@ Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/ephemeral/
/Flutter/flutter_export_environment.sh

View File

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

View File

@ -1,15 +1,15 @@
#import "NativeCryptoIosPlugin.h"
#if __has_include(<native_crypto_ios/native_crypto_ios-Swift.h>)
#import <native_crypto_ios/native_crypto_ios-Swift.h>
#import "NativeCryptoPlugin.h"
#if __has_include(<native_crypto/native_crypto-Swift.h>)
#import <native_crypto/native_crypto-Swift.h>
#else
// Support project import fallback if the generated compatibility header
// is not copied when this plugin is created as a library.
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
#import "native_crypto_ios-Swift.h"
#import "native_crypto-Swift.h"
#endif
@implementation NativeCryptoIosPlugin
@implementation NativeCryptoPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftNativeCryptoIosPlugin registerWithRegistrar:registrar];
[SwiftNativeCryptoPlugin registerWithRegistrar:registrar];
}
@end

View File

@ -0,0 +1,265 @@
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Flutter
import UIKit
import CommonCrypto
extension FlutterStandardTypedData {
var uint8Array: Array<UInt8> {
return Array(data)
}
var int8Array: Array<Int8> {
return data.withUnsafeBytes { raw in
[Int8](raw.bindMemory(to: Int8.self))
}
}
}
func crypt(operation: Int, algorithm: Int, options: Int, key: Data,
initializationVector: Data, dataIn: Data) -> Data? {
return key.withUnsafeBytes { keyUnsafeRawBufferPointer in
return dataIn.withUnsafeBytes { dataInUnsafeRawBufferPointer in
return initializationVector.withUnsafeBytes { ivUnsafeRawBufferPointer in
// Give the data out some breathing room for PKCS7's padding.
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)
}
}
}
}
func pbkdf2(hash: CCPBKDFAlgorithm, password: String, salt: String, keyByteCount: Int, rounds: Int) -> Data? {
let passwordData = password.data(using: .utf8)!
let saltData = salt.data(using: .utf8)!
var derivedKeyData = Data(repeating: 0, count: keyByteCount)
var localDerivedKeyData = derivedKeyData
let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in
saltData.withUnsafeBytes { saltBytes in
CCKeyDerivationPBKDF(
CCPBKDFAlgorithm(kCCPBKDF2),
password, passwordData.count,
saltBytes, saltData.count,
hash,
UInt32(rounds),
derivedKeyBytes, localDerivedKeyData.count)
}
}
if (derivationStatus != kCCSuccess) {
print("Error: \(derivationStatus)")
return nil;
}
return derivedKeyData
}
func pbkdf2sha256(password: String, salt: String, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash: CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256), password: password, salt: salt, keyByteCount: keyByteCount, rounds: rounds)
}
func randomGenerateBytes(count: Int) -> Data? {
let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1)
defer { bytes.deallocate() }
let status = CCRandomGenerateBytes(bytes, count)
guard status == kCCSuccess else { return nil }
return Data(bytes: bytes, count: count)
}
extension Data {
/// Encrypts for you with all the good options turned on: CBC, an IV, PKCS7
/// padding (so your input data doesn't have to be any particular length).
/// Key can be 128, 192, or 256 bits.
/// Generates a fresh IV for you each time, and prefixes it to the
/// returned ciphertext.
func encryptAES256_CBC_PKCS7_IV(key: Data) -> Data? {
guard let iv = randomGenerateBytes(count: kCCBlockSizeAES128) else { return nil }
// No option is needed for CBC, it is on by default.
guard let ciphertext = crypt(operation: kCCEncrypt,
algorithm: kCCAlgorithmAES,
options: kCCOptionPKCS7Padding,
key: key,
initializationVector: iv,
dataIn: self) else { return nil }
return iv + ciphertext
}
/// Decrypts self, where self is the IV then the ciphertext.
/// Key can be 128/192/256 bits.
func decryptAES256_CBC_PKCS7_IV(key: Data) -> Data? {
guard count > kCCBlockSizeAES128 else { return nil }
let iv = prefix(kCCBlockSizeAES128)
let ciphertext = suffix(from: kCCBlockSizeAES128)
return crypt(operation: kCCDecrypt, algorithm: kCCAlgorithmAES,
options: kCCOptionPKCS7Padding, key: key, initializationVector: iv,
dataIn: ciphertext)
}
enum Algorithm {
case sha256
var digestLength: Int {
switch self {
case .sha256: return Int(CC_SHA256_DIGEST_LENGTH)
}
}
}
func hash(for algorithm: Algorithm) -> Data {
let hashBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: algorithm.digestLength)
defer { hashBytes.deallocate() }
switch algorithm {
case .sha256:
withUnsafeBytes { (buffer) -> Void in
CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes)
}
}
return Data(bytes: hashBytes, count: algorithm.digestLength)
}
}
public class SwiftNativeCryptoPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "native.crypto.helper", binaryMessenger: registrar.messenger())
let instance = SwiftNativeCryptoPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
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 keyBytes = pbkdf2sha256(password: password, salt: salt, keyByteCount: keyLength.intValue, rounds: iteration.intValue)
if keyBytes != nil {
result(FlutterStandardTypedData.init(bytes: keyBytes!))
} else {
result(FlutterError(code: "PBKDF2ERROR",
message: "PBKDF2 KEY IS NIL.",
details: nil))
}
case "symKeygen":
let args = call.arguments as! NSDictionary
let keySize = args["size"] as! NSNumber
let keyBytes = symKeygen(keySize: keySize)
if keyBytes != nil {
result(FlutterStandardTypedData.init(bytes: keyBytes!))
} else {
result(FlutterError(code: "SYMKEYGENERROR",
message: "GENERATED KEY IS NIL.",
details: nil))
}
case "symEncrypt":
let args = call.arguments as! NSDictionary
let payload = (args["payload"] as! FlutterStandardTypedData).data
let aesKey = (args["aesKey"] as! FlutterStandardTypedData).data
let encryptedPayloadIV = symEncrypt(payload: payload, aesKey: aesKey)
result(encryptedPayloadIV)
case "symDecrypt":
let args = call.arguments as! NSDictionary
let payload = args["payload"] as! NSArray
let encrypted = (payload[0] as! FlutterStandardTypedData).data
let iv = (payload[1] as! FlutterStandardTypedData).data
let encryptedPayload = [encrypted, iv]
let aesKey = (args["aesKey"] as! FlutterStandardTypedData).data
let decryptedPayload = symDecrypt(payload: encryptedPayload, aesKey: aesKey)
if decryptedPayload != nil {
result(FlutterStandardTypedData.init(bytes: decryptedPayload!))
} else {
result(FlutterError(code: "DECRYPTIONERROR",
message: "DECRYPTED PAYLOAD IS NIL. MAYBE VERIFICATION MAC IS UNVALID.",
details: nil))
}
default: result(FlutterMethodNotImplemented)
}
}
func digest(input : Data) -> Data {
let hashed = input.hash(for: .sha256)
return hashed
}
func symKeygen(keySize : NSNumber) -> Data? {
var bytes = [Int8](repeating: 0, count: keySize.intValue / 8)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
if status == errSecSuccess { // Always test the status.
let keyBytes = bytes.withUnsafeBytes {return Data(Array($0))}
return keyBytes
}
return nil
}
func symEncrypt(payload : Data, aesKey : Data) -> [Data] {
let mac = digest(input: aesKey + payload)
let dataToEncrypt = mac + payload
var encrypted = dataToEncrypt.encryptAES256_CBC_PKCS7_IV(key: aesKey)!
// Create a range based on the length of data to return
let range = 0..<16
// Get a new copy of data
let iv = encrypted.subdata(in: range)
encrypted.removeSubrange(range)
return [encrypted, iv]
}
func symDecrypt(payload : [Data], aesKey : Data) -> Data? {
let encrypted = payload[1] + payload[0]
var decrypted = encrypted.decryptAES256_CBC_PKCS7_IV(key: aesKey)!
// 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 verificationMac = digest(input: aesKey + decrypted)
if (mac.base64EncodedData() == verificationMac.base64EncodedData()) {
return decrypted
} else {
return nil
}
}
}

View File

@ -1,9 +1,9 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint native_crypto_ios.podspec` to validate before publishing.
# Run `pod lib lint native_crypto.podspec' to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'native_crypto_ios'
s.name = 'native_crypto'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = <<-DESC
@ -15,9 +15,9 @@ A new flutter plugin project.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.platform = :ios, '13.0'
s.platform = :ios, '8.0'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
s.swift_version = '5.0'
end

22
lib/exceptions.dart Normal file
View File

@ -0,0 +1,22 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
class KeyException implements Exception {
String message;
KeyException(this.message);
}
class EncryptionException implements Exception {
String message;
EncryptionException(this.message);
}
class DecryptionException implements Exception {
String message;
DecryptionException(this.message);
}
class NotImplementedException implements Exception {
String message;
NotImplementedException(this.message);
}

View File

@ -0,0 +1,87 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:native_crypto/exceptions.dart';
/// [Sources]
/// Plugin class.
/// Contains raw functions and specific platform calls.
///
/// Use [symmetrical_crypto] for an **AES Layer API**.
class NativeCrypto {
/// [Private]
/// Contains the channel for platform specific code.
static const MethodChannel _channel =
const MethodChannel('native.crypto.helper');
/// PBKDF2.
///
/// [keyLength] is in Bytes.
/// It returns an `Uint8List`.
Future<Uint8List> pbkdf2(String password, String salt, {int keyLength: 32, int iteration: 10000}) async {
Uint8List key;
try {
key = await _channel.invokeMethod('pbkdf2', <String, dynamic>{
'password': password,
'salt': salt,
'keyLength': keyLength,
'iteration': iteration,
});
} on PlatformException catch (e) {
throw e;
}
return key;
}
/// Generates AES key.
///
/// [size] is in bits, 128, 192 or 256.
/// It returns an `Uint8List`.
Future<Uint8List> symKeygen(int size) async {
Uint8List aesKey;
try {
aesKey = await _channel.invokeMethod('symKeygen', <String, dynamic>{
'size': size,
});
} on PlatformException catch (e) {
throw e;
}
return aesKey;
}
/// Encrypts passed data with a given AES key.
///
/// Generates a random **IV**. Returns a list
/// of `Uint8List` with encrypted cipher as first
/// and IV as second member.
Future<List<Uint8List>> symEncrypt(
Uint8List payloadbytes, Uint8List aesKey) async {
final List<Uint8List> encryptedPayload =
await _channel.invokeListMethod('symEncrypt', <String, dynamic>{
'payload': payloadbytes,
'aesKey': aesKey,
});
return encryptedPayload;
}
/// Decrypts a passed payload with a given AES key.
///
/// The payload must be a list of `Uint8List`
/// with encrypted cipher as first and IV as second member.
Future<Uint8List> symDecrypt(
List<Uint8List> payloadbytes, Uint8List aesKey) async {
Uint8List decryptedPayload;
try {
decryptedPayload = await _channel.invokeMethod('symDecrypt', <String, dynamic>{
'payload': payloadbytes,
'aesKey': aesKey,
});
} on PlatformException catch (e) {
throw DecryptionException(e.message);
}
return decryptedPayload;
}
}

195
lib/symmetric_crypto.dart Normal file
View File

@ -0,0 +1,195 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'dart:async';
import 'dart:developer';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'src/native_crypto.dart';
import 'exceptions.dart';
const String TAG_ERROR = 'error.native_crypto.symmetric_crypto';
const String TAG_DEBUG = 'debug.native_crypto.symmetric_crypto';
/// Defines all available key sizes.
enum KeySize { bits128, bits192, bits256 }
/// Defines all available ciphers.
enum Cipher { AES }
/// Key Helper
///
/// You can generate Secret keys of different sizes.
class KeyGenerator {
/// Generate a secret key.
///
/// You can specify a `keySize`. Default is 256 bits.
/// It returns an `Uint8List`.
Future<Uint8List> secretKey({KeySize keySize}) async {
int size;
switch (keySize) {
case KeySize.bits128:
size = 128;
break;
case KeySize.bits192:
size = 192;
break;
case KeySize.bits256:
size = 256;
break;
default:
// Default size = 256 bits
size = 256;
break;
}
Uint8List key;
try {
key = await NativeCrypto().symKeygen(size);
log("KEY LENGTH: ${key.length}", name: TAG_DEBUG);
} on PlatformException catch (e) {
log(e.message, name: TAG_ERROR);
throw e;
}
return key;
}
/// PBKDF2.
///
/// `keyLength` is in Bytes.
/// It returns an `Uint8List`.
Future<Uint8List> pbkdf2(String password, String salt, {int keyLength: 32, int iteration: 10000}) async {
Uint8List key;
try {
key = await NativeCrypto().pbkdf2( password, salt, keyLength: keyLength, iteration: iteration);
log("PBKDF2 KEY LENGTH: ${key.length}", name: TAG_DEBUG);
} on PlatformException catch (e) {
log(e.message, name: TAG_ERROR);
throw e;
}
return key;
}
}
/// AES Helper
///
/// You can encrypt and decrypt data.
class AES {
Uint8List _key;
bool _isInitialized = false;
KeySize _keySize;
/// You can pass a key in constructor.
AES({Uint8List key}) {
this._key = key;
try {
this._isInitialized = _testKey();
} on KeyException catch (e) {
this._isInitialized = false;
throw e;
}
}
/// This key is used for encryption and decryption.
Uint8List get key => this._key;
/// Check if the AES object is intialized with a valid key before perform any operation.
bool get isInitialized => this._isInitialized;
/// Defines the size of the generated or passed key.
KeySize get keySize => this._keySize;
/// [Private]
/// Tests if the key is valid.
///
/// The key must be 128, 192 or 256 bits long.
bool _testKey() {
if (this.key != null) {
switch (this.key.length) {
case 16:
this._keySize = KeySize.bits128;
break;
case 24:
this._keySize = KeySize.bits192;
break;
case 32:
this._keySize = KeySize.bits256;
break;
default:
var error = 'Invalid key length: ${this.key.length} Bytes';
log(error, name: TAG_ERROR);
throw KeyException(error);
}
return true;
} else {
return false;
}
}
/// Generate an AES key.
///
/// You have to specify a `keySize`.
/// Return `null` if the key is already set.
init(KeySize keySize) async {
if (this.key != null) return null;
this._keySize = keySize;
try {
this._key = await KeyGenerator().secretKey(keySize: keySize);
} on PlatformException catch (e) {
log(e.message, name: TAG_ERROR);
}
try {
this._isInitialized = _testKey();
} on KeyException catch (e) {
this._isInitialized = false;
throw e;
}
}
/// Encrypts data.
///
/// Takes `Uint8List` data as parameter.
/// And returns an `Uint8List` **list**.
///
/// You can pass a different key.
///
/// The first member of this list is the `cipher data`,
/// and the second member is the `IV`.
Future<List<Uint8List>> encrypt(Uint8List data, {Uint8List key}) async {
if (!this._isInitialized && key == null)
throw EncryptionException(
'Instance not initialized. You can pass a key directly in encrypt method.');
List<Uint8List> encryptedPayload =
await NativeCrypto().symEncrypt(data, key ?? this.key);
return encryptedPayload;
}
/// Decrypts data.
///
/// Takes `Uint8List` **list** as parameter.
/// And returns plain text data as `Uint8List`.
///
/// You can pass a different key.
Future<Uint8List> decrypt(List<Uint8List> encryptedPayload, {Uint8List key}) async {
if (!this._isInitialized && key == null)
throw DecryptionException(
'Instance not initialized. You can pass a key directly in decrypt method.');
Uint8List decryptedPayload;
try {
decryptedPayload = await NativeCrypto().symDecrypt(encryptedPayload, key ?? this.key);
} on DecryptionException catch (e) {
log(e.message, name: TAG_ERROR);
throw e;
}
return decryptedPayload;
}
}

View File

@ -1,66 +0,0 @@
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

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: cf4400006550b70f28e4b4af815151d1e74846c6
channel: stable
project_type: plugin

View File

@ -1,63 +0,0 @@
## 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

@ -1,23 +0,0 @@
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

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

View File

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

View File

@ -1,30 +0,0 @@
# 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

@ -1,15 +0,0 @@
## 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

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

View File

@ -1,13 +0,0 @@
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

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

View File

@ -1,12 +0,0 @@
<?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

@ -1,18 +0,0 @@
<?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

@ -1,18 +0,0 @@
<?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>

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