diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..c6cbe56
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,8 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..3aa57e5
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,44 @@
+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 28
+
+ 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"
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..38c8d45
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..01a286e
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..430c95a
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'native_crypto'
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fcd47dc
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt
new file mode 100644
index 0000000..b05f8c0
--- /dev/null
+++ b/android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020
+ * Author: Hugo Pointcheval
+ */
+
+package fr.pointcheval.native_crypto
+
+import androidx.annotation.NonNull
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
+import io.flutter.plugin.common.MethodChannel.Result
+import io.flutter.plugin.common.PluginRegistry.Registrar
+import java.security.MessageDigest
+import java.security.SecureRandom
+import javax.crypto.Cipher
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+
+/** NativeCryptoPlugin */
+public class NativeCryptoPlugin : FlutterPlugin, MethodCallHandler {
+ // CRYPTO CONSTS
+ private val HASH_FUNC = "SHA-256"
+ private val SYM_CRYPTO_METHOD = "AES"
+ private val SYM_CRYPTO_PADDING = "AES/CBC/PKCS5PADDING"
+ private val SYM_CRYPTO_BITS = 256
+
+ override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
+ val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "native.crypto.helper")
+ channel.setMethodCallHandler(NativeCryptoPlugin());
+ }
+
+ companion object {
+ @JvmStatic
+ fun registerWith(registrar: Registrar) {
+ val channel = MethodChannel(registrar.messenger(), "native.crypto.helper")
+ channel.setMethodCallHandler(NativeCryptoPlugin())
+ }
+ }
+
+ override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
+ if (call.method == "symKeygen") {
+ val aesKey = symKeygen() // Collection
+
+ if (aesKey.isNotEmpty()) {
+ result.success(aesKey)
+ } else {
+ result.error("KeygenError", "Key generation failed.", null)
+ }
+ } else if (call.method == "symEncrypt") {
+ val payload = call.argument("payload") // ByteArray
+ val aesKey = call.argument("aesKey") // ByteArray
+
+ val encryptedPayload = symEncrypt(payload!!, aesKey!!) // Collection
+
+ if (encryptedPayload.isNotEmpty()) {
+ result.success(encryptedPayload)
+ } else {
+ result.error("EncryptionError", "Encryption failed.", null)
+ }
+ } else if (call.method == "symDecrypt") {
+ val payload = call.argument>("payload") // Collection
+ val aesKey = call.argument("aesKey") // ByteArray
+
+ val decryptedPayload = symDecrypt(payload!!, aesKey!!) // ByteArray
+
+ if (decryptedPayload != null && decryptedPayload.isNotEmpty()) {
+ result.success(decryptedPayload)
+ } else {
+ result.error("DecryptionError", "Decryption failed.", null)
+ }
+ } else {
+ result.notImplemented()
+ }
+ }
+
+ override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
+ }
+
+ // CRYPTO NATIVE FUNCTIONS
+
+ private fun digest(obj: ByteArray?): ByteArray {
+ val md = MessageDigest.getInstance(HASH_FUNC)
+ return md.digest(obj)
+ }
+
+ private fun symKeygen(): ByteArray {
+
+ val secureRandom = SecureRandom()
+ val keyGenerator = KeyGenerator.getInstance(SYM_CRYPTO_METHOD)
+ keyGenerator?.init(SYM_CRYPTO_BITS, secureRandom)
+ val skey = keyGenerator?.generateKey()
+
+ return skey!!.encoded
+ }
+
+
+ private fun symEncrypt(payload: ByteArray, aesKey: ByteArray): Collection {
+
+ val mac = digest(aesKey + payload)
+ val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
+
+ val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING)
+ cipher.init(Cipher.ENCRYPT_MODE, key)
+
+ val encryptedBytes = cipher.doFinal(mac + payload)
+ val iv = cipher.iv
+
+ return listOf(encryptedBytes, iv);
+ }
+
+ private fun symDecrypt(payload: Collection, aesKey: ByteArray): ByteArray? {
+
+ val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
+ val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING);
+ val iv = payload.last();
+ val ivSpec = IvParameterSpec(iv)
+ cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
+
+ val decryptedBytes = cipher.doFinal(payload.first());
+
+ val mac = decryptedBytes.copyOfRange(0, 32)
+ val decryptedContent = decryptedBytes.copyOfRange(32, decryptedBytes.size)
+ val verificationMac = digest(aesKey + decryptedContent)
+
+ if (mac.contentEquals(verificationMac)) return decryptedContent
+
+ return null;
+ }
+
+}
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 0000000..aa479fd
--- /dev/null
+++ b/ios/.gitignore
@@ -0,0 +1,37 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+
+.generated/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
+/Flutter/Generated.xcconfig
+/Flutter/flutter_export_environment.sh
\ No newline at end of file
diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/ios/Classes/NativeCryptoPlugin.h b/ios/Classes/NativeCryptoPlugin.h
new file mode 100644
index 0000000..0f584e1
--- /dev/null
+++ b/ios/Classes/NativeCryptoPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface NativeCryptoPlugin : NSObject
+@end
diff --git a/ios/Classes/NativeCryptoPlugin.m b/ios/Classes/NativeCryptoPlugin.m
new file mode 100644
index 0000000..c674cf7
--- /dev/null
+++ b/ios/Classes/NativeCryptoPlugin.m
@@ -0,0 +1,15 @@
+#import "NativeCryptoPlugin.h"
+#if __has_include()
+#import
+#else
+// Support project import fallback if the generated compatibility header
+// is not copied when this plugin is created as a library.
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
+#import "native_crypto-Swift.h"
+#endif
+
+@implementation NativeCryptoPlugin
++ (void)registerWithRegistrar:(NSObject*)registrar {
+ [SwiftNativeCryptoPlugin registerWithRegistrar:registrar];
+}
+@end
diff --git a/ios/Classes/SwiftNativeCryptoPlugin.swift b/ios/Classes/SwiftNativeCryptoPlugin.swift
new file mode 100644
index 0000000..d24db65
--- /dev/null
+++ b/ios/Classes/SwiftNativeCryptoPlugin.swift
@@ -0,0 +1,14 @@
+import Flutter
+import UIKit
+
+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) {
+ result("iOS " + UIDevice.current.systemVersion)
+ }
+}
diff --git a/ios/native_crypto.podspec b/ios/native_crypto.podspec
new file mode 100644
index 0000000..0ff9ea5
--- /dev/null
+++ b/ios/native_crypto.podspec
@@ -0,0 +1,23 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint native_crypto.podspec' to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'native_crypto'
+ s.version = '0.0.1'
+ s.summary = 'A new flutter plugin project.'
+ s.description = <<-DESC
+A new flutter plugin project.
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.dependency 'Flutter'
+ s.platform = :ios, '8.0'
+
+ # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
+ s.swift_version = '5.0'
+end
diff --git a/lib/native_crypto.dart b/lib/native_crypto.dart
new file mode 100644
index 0000000..d515a4a
--- /dev/null
+++ b/lib/native_crypto.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2020
+// Author: Hugo Pointcheval
+import 'dart:async';
+import 'dart:typed_data';
+
+import 'package:flutter/services.dart';
+
+class NativeCrypto {
+ static const MethodChannel _channel =
+ const MethodChannel('native.crypto.helper');
+
+ Future sumKeygen() async {
+ final Uint8List aesKey = await _channel.invokeMethod('symKeygen');
+
+ return aesKey;
+ }
+
+ Future> symEncrypt(
+ Uint8List payloadbytes, Uint8List aesKey) async {
+ final List encyptedPayload =
+ await _channel.invokeListMethod('symEncrypt', {
+ 'payload': payloadbytes,
+ 'aesKey': aesKey,
+ });
+
+ return encyptedPayload;
+ }
+
+ Future symDecrypt(
+ List payloadbytes, Uint8List aesKey) async {
+ final Uint8List decryptedPayload =
+ await _channel.invokeMethod('symDecrypt', {
+ 'payload': payloadbytes,
+ 'aesKey': aesKey,
+ });
+
+ return decryptedPayload;
+ }
+}