Browse Source

Add plugin sources

Hugo Pointcheval 5 months ago
parent
commit
842c9ecf4f

+ 8 - 0
android/.gitignore

@@ -0,0 +1,8 @@
1
+*.iml
2
+.gradle
3
+/local.properties
4
+/.idea/workspace.xml
5
+/.idea/libraries
6
+.DS_Store
7
+/build
8
+/captures

+ 44 - 0
android/build.gradle

@@ -0,0 +1,44 @@
1
+group 'fr.pointcheval.native_crypto'
2
+version '1.0-SNAPSHOT'
3
+
4
+buildscript {
5
+    ext.kotlin_version = '1.3.50'
6
+    repositories {
7
+        google()
8
+        jcenter()
9
+    }
10
+
11
+    dependencies {
12
+        classpath 'com.android.tools.build:gradle:3.5.0'
13
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
+    }
15
+}
16
+
17
+rootProject.allprojects {
18
+    repositories {
19
+        google()
20
+        jcenter()
21
+    }
22
+}
23
+
24
+apply plugin: 'com.android.library'
25
+apply plugin: 'kotlin-android'
26
+
27
+android {
28
+    compileSdkVersion 28
29
+
30
+    sourceSets {
31
+        main.java.srcDirs += 'src/main/kotlin'
32
+    }
33
+    defaultConfig {
34
+        minSdkVersion 16
35
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
36
+    }
37
+    lintOptions {
38
+        disable 'InvalidPackage'
39
+    }
40
+}
41
+
42
+dependencies {
43
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
44
+}

+ 4 - 0
android/gradle.properties

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

+ 5 - 0
android/gradle/wrapper/gradle-wrapper.properties

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

+ 1 - 0
android/settings.gradle

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

+ 3 - 0
android/src/main/AndroidManifest.xml

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

+ 135 - 0
android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt

@@ -0,0 +1,135 @@
1
+/*
2
+ * Copyright (c) 2020
3
+ * Author: Hugo Pointcheval
4
+ */
5
+
6
+package fr.pointcheval.native_crypto
7
+
8
+import androidx.annotation.NonNull
9
+import io.flutter.embedding.engine.plugins.FlutterPlugin
10
+import io.flutter.plugin.common.MethodCall
11
+import io.flutter.plugin.common.MethodChannel
12
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
13
+import io.flutter.plugin.common.MethodChannel.Result
14
+import io.flutter.plugin.common.PluginRegistry.Registrar
15
+import java.security.MessageDigest
16
+import java.security.SecureRandom
17
+import javax.crypto.Cipher
18
+import javax.crypto.KeyGenerator
19
+import javax.crypto.SecretKey
20
+import javax.crypto.spec.IvParameterSpec
21
+import javax.crypto.spec.SecretKeySpec
22
+
23
+
24
+/** NativeCryptoPlugin */
25
+public class NativeCryptoPlugin : FlutterPlugin, MethodCallHandler {
26
+    // CRYPTO CONSTS
27
+    private val HASH_FUNC = "SHA-256"
28
+    private val SYM_CRYPTO_METHOD = "AES"
29
+    private val SYM_CRYPTO_PADDING = "AES/CBC/PKCS5PADDING"
30
+    private val SYM_CRYPTO_BITS = 256
31
+
32
+    override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
33
+        val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "native.crypto.helper")
34
+        channel.setMethodCallHandler(NativeCryptoPlugin());
35
+    }
36
+
37
+    companion object {
38
+        @JvmStatic
39
+        fun registerWith(registrar: Registrar) {
40
+            val channel = MethodChannel(registrar.messenger(), "native.crypto.helper")
41
+            channel.setMethodCallHandler(NativeCryptoPlugin())
42
+        }
43
+    }
44
+
45
+    override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
46
+        if (call.method == "symKeygen") {
47
+            val aesKey = symKeygen() // Collection<ByteArray>
48
+
49
+            if (aesKey.isNotEmpty()) {
50
+                result.success(aesKey)
51
+            } else {
52
+                result.error("KeygenError", "Key generation failed.", null)
53
+            }
54
+        } else if (call.method == "symEncrypt") {
55
+            val payload = call.argument<ByteArray>("payload") // ByteArray
56
+            val aesKey = call.argument<ByteArray>("aesKey") // ByteArray
57
+
58
+            val encryptedPayload = symEncrypt(payload!!, aesKey!!) // Collection<ByteArray>
59
+
60
+            if (encryptedPayload.isNotEmpty()) {
61
+                result.success(encryptedPayload)
62
+            } else {
63
+                result.error("EncryptionError", "Encryption failed.", null)
64
+            }
65
+        } else if (call.method == "symDecrypt") {
66
+            val payload = call.argument<Collection<ByteArray>>("payload") // Collection<ByteArray>
67
+            val aesKey = call.argument<ByteArray>("aesKey") // ByteArray
68
+
69
+            val decryptedPayload = symDecrypt(payload!!, aesKey!!) // ByteArray
70
+
71
+            if (decryptedPayload != null && decryptedPayload.isNotEmpty()) {
72
+                result.success(decryptedPayload)
73
+            } else {
74
+                result.error("DecryptionError", "Decryption failed.", null)
75
+            }
76
+        } else {
77
+            result.notImplemented()
78
+        }
79
+    }
80
+
81
+    override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
82
+    }
83
+
84
+    // CRYPTO NATIVE FUNCTIONS
85
+
86
+    private fun digest(obj: ByteArray?): ByteArray {
87
+        val md = MessageDigest.getInstance(HASH_FUNC)
88
+        return md.digest(obj)
89
+    }
90
+
91
+    private fun symKeygen(): ByteArray {
92
+
93
+        val secureRandom = SecureRandom()
94
+        val keyGenerator = KeyGenerator.getInstance(SYM_CRYPTO_METHOD)
95
+        keyGenerator?.init(SYM_CRYPTO_BITS, secureRandom)
96
+        val skey = keyGenerator?.generateKey()
97
+
98
+        return skey!!.encoded
99
+    }
100
+
101
+
102
+    private fun symEncrypt(payload: ByteArray, aesKey: ByteArray): Collection<ByteArray> {
103
+
104
+        val mac = digest(aesKey + payload)
105
+        val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
106
+
107
+        val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING)
108
+        cipher.init(Cipher.ENCRYPT_MODE, key)
109
+
110
+        val encryptedBytes = cipher.doFinal(mac + payload)
111
+        val iv = cipher.iv
112
+
113
+        return listOf(encryptedBytes, iv);
114
+    }
115
+
116
+    private fun symDecrypt(payload: Collection<ByteArray>, aesKey: ByteArray): ByteArray? {
117
+
118
+        val key: SecretKey = SecretKeySpec(aesKey, SYM_CRYPTO_METHOD)
119
+        val cipher = Cipher.getInstance(SYM_CRYPTO_PADDING);
120
+        val iv = payload.last();
121
+        val ivSpec = IvParameterSpec(iv)
122
+        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
123
+
124
+        val decryptedBytes = cipher.doFinal(payload.first());
125
+
126
+        val mac = decryptedBytes.copyOfRange(0, 32)
127
+        val decryptedContent = decryptedBytes.copyOfRange(32, decryptedBytes.size)
128
+        val verificationMac = digest(aesKey + decryptedContent)
129
+
130
+        if (mac.contentEquals(verificationMac)) return decryptedContent
131
+
132
+        return null;
133
+    }
134
+
135
+}

+ 37 - 0
ios/.gitignore

@@ -0,0 +1,37 @@
1
+.idea/
2
+.vagrant/
3
+.sconsign.dblite
4
+.svn/
5
+
6
+.DS_Store
7
+*.swp
8
+profile
9
+
10
+DerivedData/
11
+build/
12
+GeneratedPluginRegistrant.h
13
+GeneratedPluginRegistrant.m
14
+
15
+.generated/
16
+
17
+*.pbxuser
18
+*.mode1v3
19
+*.mode2v3
20
+*.perspectivev3
21
+
22
+!default.pbxuser
23
+!default.mode1v3
24
+!default.mode2v3
25
+!default.perspectivev3
26
+
27
+xcuserdata
28
+
29
+*.moved-aside
30
+
31
+*.pyc
32
+*sync/
33
+Icon?
34
+.tags*
35
+
36
+/Flutter/Generated.xcconfig
37
+/Flutter/flutter_export_environment.sh

+ 0 - 0
ios/Assets/.gitkeep


+ 4 - 0
ios/Classes/NativeCryptoPlugin.h

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

+ 15 - 0
ios/Classes/NativeCryptoPlugin.m

@@ -0,0 +1,15 @@
1
+#import "NativeCryptoPlugin.h"
2
+#if __has_include(<native_crypto/native_crypto-Swift.h>)
3
+#import <native_crypto/native_crypto-Swift.h>
4
+#else
5
+// Support project import fallback if the generated compatibility header
6
+// is not copied when this plugin is created as a library.
7
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
8
+#import "native_crypto-Swift.h"
9
+#endif
10
+
11
+@implementation NativeCryptoPlugin
12
++ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
13
+  [SwiftNativeCryptoPlugin registerWithRegistrar:registrar];
14
+}
15
+@end

+ 14 - 0
ios/Classes/SwiftNativeCryptoPlugin.swift

@@ -0,0 +1,14 @@
1
+import Flutter
2
+import UIKit
3
+
4
+public class SwiftNativeCryptoPlugin: NSObject, FlutterPlugin {
5
+  public static func register(with registrar: FlutterPluginRegistrar) {
6
+    let channel = FlutterMethodChannel(name: "native_crypto", binaryMessenger: registrar.messenger())
7
+    let instance = SwiftNativeCryptoPlugin()
8
+    registrar.addMethodCallDelegate(instance, channel: channel)
9
+  }
10
+
11
+  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12
+    result("iOS " + UIDevice.current.systemVersion)
13
+  }
14
+}

+ 23 - 0
ios/native_crypto.podspec

@@ -0,0 +1,23 @@
1
+#
2
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3
+# Run `pod lib lint native_crypto.podspec' to validate before publishing.
4
+#
5
+Pod::Spec.new do |s|
6
+  s.name             = 'native_crypto'
7
+  s.version          = '0.0.1'
8
+  s.summary          = 'A new flutter plugin project.'
9
+  s.description      = <<-DESC
10
+A new flutter plugin project.
11
+                       DESC
12
+  s.homepage         = 'http://example.com'
13
+  s.license          = { :file => '../LICENSE' }
14
+  s.author           = { 'Your Company' => 'email@example.com' }
15
+  s.source           = { :path => '.' }
16
+  s.source_files = 'Classes/**/*'
17
+  s.dependency 'Flutter'
18
+  s.platform = :ios, '8.0'
19
+
20
+  # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
21
+  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
22
+  s.swift_version = '5.0'
23
+end

+ 39 - 0
lib/native_crypto.dart

@@ -0,0 +1,39 @@
1
+// Copyright (c) 2020
2
+// Author: Hugo Pointcheval
3
+import 'dart:async';
4
+import 'dart:typed_data';
5
+
6
+import 'package:flutter/services.dart';
7
+
8
+class NativeCrypto {
9
+  static const MethodChannel _channel =
10
+      const MethodChannel('native.crypto.helper');
11
+
12
+  Future<Uint8List> sumKeygen() async {
13
+    final Uint8List aesKey = await _channel.invokeMethod('symKeygen');
14
+
15
+    return aesKey;
16
+  }
17
+
18
+  Future<List<Uint8List>> symEncrypt(
19
+      Uint8List payloadbytes, Uint8List aesKey) async {
20
+    final List<Uint8List> encyptedPayload =
21
+        await _channel.invokeListMethod('symEncrypt', <String, dynamic>{
22
+      'payload': payloadbytes,
23
+      'aesKey': aesKey,
24
+    });
25
+
26
+    return encyptedPayload;
27
+  }
28
+
29
+  Future<Uint8List> symDecrypt(
30
+      List<Uint8List> payloadbytes, Uint8List aesKey) async {
31
+    final Uint8List decryptedPayload =
32
+        await _channel.invokeMethod('symDecrypt', <String, dynamic>{
33
+      'payload': payloadbytes,
34
+      'aesKey': aesKey,
35
+    });
36
+
37
+    return decryptedPayload;
38
+  }
39
+}