Compare commits

..

5 Commits

Author SHA1 Message Date
9e0d921564 Switch to Federated Plugin layout 2021-12-27 21:13:26 +01:00
9790be4a5c Update readme 2021-05-08 20:21:43 +02:00
be3ed99379 Update exemple app 2021-05-08 20:11:45 +02:00
871e3b74ed Add RSA keys generation 2021-05-08 20:11:28 +02:00
36e942f41e Rework key size 2021-05-08 20:10:49 +02:00
202 changed files with 3337 additions and 3384 deletions

View File

@ -1,25 +0,0 @@
## 0.0.6
**WIP...**
## 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.

170
README.md
View File

@ -1,170 +0,0 @@
# NativeCrypto for Flutter
![NativeCrypto Logo](/assets/native_crypto.png)
---
Fast and powerful cryptographic functions thanks to **javax.crypto** and **CommonCrypto**.
## 📝 Table of Contents
- [About](#about)
- [Getting Started](#getting_started)
- [Example](#example)
- [Usage](#usage)
- [Built Using](#built_using)
- [TODOS](#todos)
- [Authors](#authors)
## 🧐 About <a name = "about"></a>
The goal of this plugin is to provide simple access to fast and powerful cryptographic functions by calling native libraries. So on **Android** the plugin uses *javax.crypto* and on **iOS** it uses *CommonCrypto*.
I started this project because using **Pointy Castle** I faced big performance issues on smartphone. It's quite simple, an encryption of 1MB of data in AES256 on an Android device takes **20s** with Pointy Castle against **27ms** using NativeCrypto.
![Pointy Castle Benchmark](/assets/benchmark_pointycastle.png)
> We also notice on this benchmark that the AES encryption time does not even increase linearly with size.
As for NativeCrypto, here is a benchmark realized on an Android device, Huawei P30 Pro.
| Size (kB) | NativeCrypto **encryption** time (ms) |
|-----------|---------------------------------------|
| 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
> Less than 1s for 50 mB.
In short, **NativeCrypto** is incomparable to **Pointy Castle** in terms of performance.
## 🏁 Getting Started <a name = "getting_started"></a>
### Prerequisites
You'll need:
- Flutter
### Installing
Add these lines in your **pubspec.yaml**:
```yaml
native_crypto:
git:
url: https://gogs.pointcheval.fr/hugo/native-crypto-flutter.git
ref: v0.0.x
```
> Replace "x" with the current version!
Then in your code:
```dart
import 'package:native_crypto/native_crypto.dart';
```
## 🔍 Example <a name="example"></a>
Look in **example/lib/** for an example app.
## 🎈 Usage <a name="usage"></a>
To derive a key with **PBKDF2**.
```dart
PBKDF2 _pbkdf2 = PBKDF2(keyLength: 32, iteration: 1000, hash: HashAlgorithm.SHA512);
await _pbkdf2.derive(password: "password123", salt: 'salty');
SecretKey key = _pbkdf2.key;
```
To generate a key, and create an **AES Cipher** instance.
```dart
AESCipher aes = await AESCipher.generate(
AESKeySize.bits256,
CipherParameters(
BlockCipherMode.CBC,
PlainTextPadding.PKCS5,
),
);
```
You can also generate key, then create **AES Cipher**.
```dart
SecretKey _key = await SecretKey.generate(256, CipherAlgorithm.AES);
AESCipher aes = AESCipher(
_key,
CipherParameters(
BlockCipherMode.CBC,
PlainTextPadding.PKCS5,
),
);
```
Then you can encrypt/decrypt data with this cipher.
```dart
CipherText cipherText = await aes.encrypt(data);
Uint8List plainText = await aes.decrypt(cipherText);
```
You can easely get encrypted bytes and IV from a CipherText
```dart
Uint8List bytes = cipherText.bytes;
Uint8List iv = cipherText.iv;
```
To create a cipher text with custom data.
```dart
CipherText cipherText = AESCipherText(bytes, iv);
```
To create a hashed message
```dart
MessageDigest md = MessageDigest.getInstance("sha256");
Uint8List hash = await md.digest(message);
```
## ⛏️ Built Using <a name = "built_using"></a>
- [Dart](https://dart.dev)
- [Flutter](https://flutter.dev) - Framework
- [Kotlin](https://kotlinlang.org) - Android Specific code
- [Swift](https://www.apple.com/fr/swift/) - iOS Specific code
## 🚀 TODOS <a name = "todos">
Here you can check major changes, roadmap and todos.
Once the **BlowFish algorithm** is exposed on Android and iOS, I plan to deal with asymmetric cryptography with the implementation of a Key Encapsulation Mechanism.
- [x] Add PBKDF2 support.
- [x] Implement working cross platform AES encryption/decryption.
- [x] Add Different key sizes support.
- [x] Add exceptions.
- [x] Clean platform specific code.
- [x] Add digest.
- [x] Rework exposed API.
- [ ] Implement BlowFish.
- [ ] Add KeyPair generation.
- [ ] Add KEM.
- [ ] Porting NativeCrypto to other platforms...
You can contribute to this project.
## ✍️ Authors <a name = "authors"></a>
- [Hugo Pointcheval](https://github.com/hugo-pcl) - Idea & Initial work

8
android/.gitignore vendored
View File

@ -1,8 +0,0 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

View File

@ -1,44 +0,0 @@
group 'fr.pointcheval.native_crypto'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 29
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
2f8ea5763cdbee83dd665ad298f0e380

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,69 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:flutter/material.dart';
import 'package:native_crypto_example/pages/kemPage.dart';
import 'pages/benchmarkPage.dart';
import 'pages/cipherPage.dart';
import 'pages/hashKeyDerivationPage.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _currentIndex = 0;
final List<Widget> _children = [
HashKeyDerivationPage(),
CipherPage(),
KemPage(),
BenchmarkPage(),
];
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Native Crypto'),
),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.black,
showUnselectedLabels: true,
onTap: onTabTapped, // new
currentIndex: _currentIndex, // new
items: [
BottomNavigationBarItem(
icon: Icon(Icons.vpn_key),
label: 'Key',
),
BottomNavigationBarItem(
icon: Icon(Icons.lock),
label: 'Encryption',
),
BottomNavigationBarItem(
icon: Icon(Icons.connect_without_contact),
label: 'KEM',
),
BottomNavigationBarItem(
icon: Icon(Icons.timer),
label: 'Benchmark',
),
],
),
),
);
}
}

View File

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

View File

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

View File

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

View File

@ -1,21 +0,0 @@
// Copyright (c) 2020
// Author: Hugo Pointcheval
import 'package:flutter/material.dart';
class KemPage extends StatefulWidget {
KemPage({Key key}) : super(key: key);
@override
_KemPageState createState() => _KemPageState();
}
class _KemPageState extends State<KemPage> {
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("Not implemented."),
),
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,25 +0,0 @@
//
// KeyGeneration.swift
//
// NativeCryptoPlugin
//
// Copyright (c) 2020
// Author: Hugo Pointcheval
//
import Foundation
class KeyGeneration {
func keygen(size : NSNumber) -> Data? {
var bytes = [Int8](repeating: 0, count: size.intValue / 8)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
if status == errSecSuccess {
let keyBytes = bytes.withUnsafeBytes {
return Data(Array($0))
}
return keyBytes
}
return nil
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

389
native_crypto/.gitignore vendored Normal file
View File

@ -0,0 +1,389 @@
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows
### Dart ###
# See https://www.dartlang.org/guides/libraries/private-files
# 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
# Directory created by dartdoc
# 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.
*.js # When generated by dart2js. Don't specify *.js if your
# project includes source files written in JavaScript.
*.js_
*.js.deps
*.js.map
.flutter-plugins
.flutter-plugins-dependencies
### Dart Patch ###
# dotenv environment variables file
.env
### Flutter ###
# Flutter/Dart/Pub related
**/doc/api/
.fvm/
.pub-cache/
.pub/
coverage/
lib/generated_plugin_registrant.dart
# For library packages, dont commit the pubspec.lock file.
# Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies.
# See https://dart.dev/guides/libraries/private-files#pubspeclock
#pubspec.lock
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/key.properties
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/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.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# 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
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Kotlin ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### 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)
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
# Pods/
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
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.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# Support for Project snippet scope
!.vscode/*.code-snippets
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)

View File

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

View File

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
native_crypto/LICENSE Normal file
View File

@ -0,0 +1 @@
TODO: Add your license here.

18
native_crypto/README.md Normal file
View File

@ -0,0 +1,18 @@
# native_crypto
A new flutter plugin project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported.
To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same
directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.

View File

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

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

View File

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

View File

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

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

View File

@ -26,24 +26,28 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 28 compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
lintOptions {
disable 'InvalidPackage'
}
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "fr.pointcheval.native_crypto_example" applicationId "fr.pointcheval.native_crypto_example"
minSdkVersion 16 minSdkVersion flutter.minSdkVersion
targetSdkVersion 28 targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -61,7 +65,4 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 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,21 +1,25 @@
<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"> package="fr.pointcheval.native_crypto_example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that <application
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:label="native_crypto_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> 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> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

View File

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 442 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

View File

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

View File

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

View File

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

View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@ -0,0 +1,484 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; 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>"; };
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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

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

View File

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

View File

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

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