Add plugin sources
This commit is contained in:
parent
bec8e2100a
commit
842c9ecf4f
8
android/.gitignore
vendored
Normal file
8
android/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
44
android/build.gradle
Normal file
44
android/build.gradle
Normal file
@ -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"
|
||||||
|
}
|
4
android/gradle.properties
Normal file
4
android/gradle.properties
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.enableR8=true
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -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
|
1
android/settings.gradle
Normal file
1
android/settings.gradle
Normal file
@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'native_crypto'
|
3
android/src/main/AndroidManifest.xml
Normal file
3
android/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="fr.pointcheval.native_crypto">
|
||||||
|
</manifest>
|
@ -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<ByteArray>
|
||||||
|
|
||||||
|
if (aesKey.isNotEmpty()) {
|
||||||
|
result.success(aesKey)
|
||||||
|
} else {
|
||||||
|
result.error("KeygenError", "Key generation failed.", null)
|
||||||
|
}
|
||||||
|
} else if (call.method == "symEncrypt") {
|
||||||
|
val payload = call.argument<ByteArray>("payload") // ByteArray
|
||||||
|
val aesKey = call.argument<ByteArray>("aesKey") // ByteArray
|
||||||
|
|
||||||
|
val encryptedPayload = symEncrypt(payload!!, aesKey!!) // Collection<ByteArray>
|
||||||
|
|
||||||
|
if (encryptedPayload.isNotEmpty()) {
|
||||||
|
result.success(encryptedPayload)
|
||||||
|
} else {
|
||||||
|
result.error("EncryptionError", "Encryption failed.", null)
|
||||||
|
}
|
||||||
|
} else if (call.method == "symDecrypt") {
|
||||||
|
val payload = call.argument<Collection<ByteArray>>("payload") // Collection<ByteArray>
|
||||||
|
val aesKey = call.argument<ByteArray>("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<ByteArray> {
|
||||||
|
|
||||||
|
val mac = digest(aesKey + payload)
|
||||||
|
val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
|
||||||
|
|
||||||
|
val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING)
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key)
|
||||||
|
|
||||||
|
val encryptedBytes = cipher.doFinal(mac + payload)
|
||||||
|
val iv = cipher.iv
|
||||||
|
|
||||||
|
return listOf(encryptedBytes, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun symDecrypt(payload: Collection<ByteArray>, aesKey: ByteArray): ByteArray? {
|
||||||
|
|
||||||
|
val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
|
||||||
|
val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING);
|
||||||
|
val iv = payload.last();
|
||||||
|
val ivSpec = IvParameterSpec(iv)
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
|
||||||
|
|
||||||
|
val decryptedBytes = cipher.doFinal(payload.first());
|
||||||
|
|
||||||
|
val mac = decryptedBytes.copyOfRange(0, 32)
|
||||||
|
val decryptedContent = decryptedBytes.copyOfRange(32, decryptedBytes.size)
|
||||||
|
val verificationMac = digest(aesKey + decryptedContent)
|
||||||
|
|
||||||
|
if (mac.contentEquals(verificationMac)) return decryptedContent
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
ios/.gitignore
vendored
Normal file
37
ios/.gitignore
vendored
Normal file
@ -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
|
0
ios/Assets/.gitkeep
Normal file
0
ios/Assets/.gitkeep
Normal file
4
ios/Classes/NativeCryptoPlugin.h
Normal file
4
ios/Classes/NativeCryptoPlugin.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
|
||||||
|
@interface NativeCryptoPlugin : NSObject<FlutterPlugin>
|
||||||
|
@end
|
15
ios/Classes/NativeCryptoPlugin.m
Normal file
15
ios/Classes/NativeCryptoPlugin.m
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#import "NativeCryptoPlugin.h"
|
||||||
|
#if __has_include(<native_crypto/native_crypto-Swift.h>)
|
||||||
|
#import <native_crypto/native_crypto-Swift.h>
|
||||||
|
#else
|
||||||
|
// Support project import fallback if the generated compatibility header
|
||||||
|
// is not copied when this plugin is created as a library.
|
||||||
|
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
|
||||||
|
#import "native_crypto-Swift.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@implementation NativeCryptoPlugin
|
||||||
|
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||||
|
[SwiftNativeCryptoPlugin registerWithRegistrar:registrar];
|
||||||
|
}
|
||||||
|
@end
|
14
ios/Classes/SwiftNativeCryptoPlugin.swift
Normal file
14
ios/Classes/SwiftNativeCryptoPlugin.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
23
ios/native_crypto.podspec
Normal file
23
ios/native_crypto.podspec
Normal file
@ -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
|
39
lib/native_crypto.dart
Normal file
39
lib/native_crypto.dart
Normal file
@ -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<Uint8List> sumKeygen() async {
|
||||||
|
final Uint8List aesKey = await _channel.invokeMethod('symKeygen');
|
||||||
|
|
||||||
|
return aesKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Uint8List>> symEncrypt(
|
||||||
|
Uint8List payloadbytes, Uint8List aesKey) async {
|
||||||
|
final List<Uint8List> encyptedPayload =
|
||||||
|
await _channel.invokeListMethod('symEncrypt', <String, dynamic>{
|
||||||
|
'payload': payloadbytes,
|
||||||
|
'aesKey': aesKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
return encyptedPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> symDecrypt(
|
||||||
|
List<Uint8List> payloadbytes, Uint8List aesKey) async {
|
||||||
|
final Uint8List decryptedPayload =
|
||||||
|
await _channel.invokeMethod('symDecrypt', <String, dynamic>{
|
||||||
|
'payload': payloadbytes,
|
||||||
|
'aesKey': aesKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
return decryptedPayload;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user