Switch to Federated Plugin layout
							
								
								
									
										25
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						| @ -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. | ||||
							
								
								
									
										167
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @ -1,167 +0,0 @@ | ||||
| # NativeCrypto for Flutter | ||||
| 
 | ||||
|  | ||||
| --- | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| > 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 iPhone 11. | ||||
| 
 | ||||
| | Size (kB) | NativeCrypto **encryption** time (ms) | | ||||
| |-----------|---------------------------------------| | ||||
| | 2 mB | 13 ms | ||||
| | 4 mB | 17 ms | ||||
| | 8 mB | 56 ms | ||||
| | 16 mB | 73 ms | ||||
| | 32 mB | 120 ms | ||||
| | 64 mB | 243 ms | ||||
| | 128 mB | 509 ms | ||||
| | 256 mB | 1057 ms | ||||
| 
 | ||||
| > ~1s for 256 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://github.com/hugo-pcl/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. | ||||
| 
 | ||||
| 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. | ||||
| - [x] 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 | ||||
| - [Chisom Maxwell](https://github.com/maxcotech) - For the chunks idea [#2](https://github.com/hugo-pcl/native-crypto-flutter/issues/2) | ||||
							
								
								
									
										8
									
								
								android/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,8 +0,0 @@ | ||||
| *.iml | ||||
| .gradle | ||||
| /local.properties | ||||
| /.idea/workspace.xml | ||||
| /.idea/libraries | ||||
| .DS_Store | ||||
| /build | ||||
| /captures | ||||
| @ -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" | ||||
| } | ||||
| @ -1 +0,0 @@ | ||||
| rootProject.name = 'native_crypto' | ||||
| @ -1,3 +0,0 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|   package="fr.pointcheval.native_crypto"> | ||||
| </manifest> | ||||
| @ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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) | ||||
|     } | ||||
| } | ||||
| @ -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!") | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
|     } | ||||
| } | ||||
| @ -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) { | ||||
|     } | ||||
| } | ||||
| @ -1,5 +0,0 @@ | ||||
| package fr.pointcheval.native_crypto | ||||
| 
 | ||||
| import java.lang.Exception | ||||
| 
 | ||||
| class PlatformVersionException(message : String) : Exception(message) | ||||
| Before Width: | Height: | Size: 120 KiB | 
| Before Width: | Height: | Size: 30 KiB | 
							
								
								
									
										7
									
								
								example/android/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,7 +0,0 @@ | ||||
| gradle-wrapper.jar | ||||
| /.gradle | ||||
| /captures/ | ||||
| /gradlew | ||||
| /gradlew.bat | ||||
| /local.properties | ||||
| GeneratedPluginRegistrant.java | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
| @ -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> | ||||
| @ -1,4 +0,0 @@ | ||||
| org.gradle.jvmargs=-Xmx1536M | ||||
| android.enableR8=true | ||||
| android.useAndroidX=true | ||||
| android.enableJetifier=true | ||||
| @ -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 | ||||
| @ -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 | ||||
| } | ||||
| @ -1 +0,0 @@ | ||||
| a1d04c54a9dc0dec55298921eadf7972 | ||||
| @ -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 | ||||
| @ -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.10.1 | ||||
| @ -1 +0,0 @@ | ||||
| #import "GeneratedPluginRegistrant.h" | ||||
| @ -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', | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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, | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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 | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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 | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -1,36 +0,0 @@ | ||||
| // Copyright (c) 2020 | ||||
| // Author: Hugo Pointcheval | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:native_crypto/native_crypto.dart'; | ||||
| 
 | ||||
| class KemPage extends StatefulWidget { | ||||
|   KemPage({key}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _KemPageState createState() => _KemPageState(); | ||||
| } | ||||
| 
 | ||||
| class _KemPageState extends State<KemPage> { | ||||
|   void test() async { | ||||
|     KeySpec specs = RSAKeySpec(2048); | ||||
|     KeyPair kp = await KeyPair.generate(specs); | ||||
|     print(kp.isComplete); | ||||
|     print(kp.privateKey); | ||||
|     print(kp.privateKey.encoded); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     test(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       child: Center( | ||||
|         child: Text("Not implemented."), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
|   } | ||||
| } | ||||
| @ -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), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -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) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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) | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
|     } | ||||
| } | ||||
| @ -1,59 +0,0 @@ | ||||
| // | ||||
| //  KeyGeneration.swift | ||||
| // | ||||
| //  NativeCryptoPlugin | ||||
| // | ||||
| //  Copyright (c) 2020 | ||||
| //  Author: Hugo Pointcheval | ||||
| // | ||||
| import Foundation | ||||
| import CommonCrypto | ||||
| 
 | ||||
| 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 | ||||
|     } | ||||
|      | ||||
|     @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) | ||||
|     func rsaKeypairGen(size : NSNumber) throws -> [Data]? { | ||||
|          | ||||
|         let tagData = UUID().uuidString.data(using: .utf8) | ||||
|          | ||||
|         let isPermanent = true | ||||
|          | ||||
|         let attributes: [CFString: Any] = [ | ||||
|             kSecAttrKeyType: kSecAttrKeyTypeRSA, | ||||
|             kSecAttrKeySizeInBits: (size.intValue * 8), | ||||
|             kSecPrivateKeyAttrs: [ | ||||
|                 kSecAttrIsPermanent: isPermanent, | ||||
|                 kSecAttrApplicationTag: tagData! | ||||
|             ] | ||||
|         ] | ||||
|          | ||||
|         var error: Unmanaged<CFError>? | ||||
|         guard let privKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { | ||||
|             throw error!.takeRetainedValue() as Error | ||||
|         } | ||||
|         let pubKey = SecKeyCopyPublicKey(privKey) | ||||
|          | ||||
|          | ||||
|         var errorExport: Unmanaged<CFError>? | ||||
|         let data1 = SecKeyCopyExternalRepresentation(pubKey!, &errorExport) | ||||
|         let unwrappedData1 = data1 as Data? | ||||
|          | ||||
|         let data2 = SecKeyCopyExternalRepresentation(privKey, &errorExport) | ||||
|         let unwrappedData2 = data2 as Data? | ||||
|          | ||||
|         return [unwrappedData1!, unwrappedData2!] | ||||
|     } | ||||
| } | ||||
| @ -1,4 +0,0 @@ | ||||
| #import <Flutter/Flutter.h> | ||||
| 
 | ||||
| @interface NativeCryptoPlugin : NSObject<FlutterPlugin> | ||||
| @end | ||||
| @ -1,158 +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 "rsaKeypairGen": | ||||
|         let args = call.arguments as! NSDictionary | ||||
|          | ||||
|         let size = args["size"] as! NSNumber | ||||
|          | ||||
|         let keypair : [Data]? | ||||
|          | ||||
|         if #available(iOS 10.0, *) { | ||||
|             do { | ||||
|                 keypair = try KeyGeneration().rsaKeypairGen(size: size) | ||||
|             } catch { | ||||
|                 keypair = nil | ||||
|             } | ||||
|         } else { | ||||
|             // Fallback on earlier versions | ||||
|             keypair = nil | ||||
|         } | ||||
|          | ||||
|         if keypair != nil { | ||||
|             result(keypair) | ||||
|         } else { | ||||
|             result(FlutterError(code: "KEYPAIRGENERROR", | ||||
|                                 message: "GENERATED KEYPAIR IS EMPTY.", | ||||
|                                 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) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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"; | ||||
| @ -1,77 +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 || _mode == KemMode.DECAPSULATION) { | ||||
|       throw KemInitException("KEM not properly initialized."); | ||||
|     } | ||||
|     throw UnimplementedError(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<SecretKey> decapsulate(Encapsulation encapsulation) { | ||||
|     if (!_isInit || _mode == KemMode.ENCAPSULATION) { | ||||
|       throw KemInitException("KEM not properly initialized."); | ||||
|     } | ||||
|     throw UnimplementedError(); | ||||
|   } | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -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; | ||||
|   } | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
							
								
								
									
										118
									
								
								lib/src/key.dart
									
									
									
									
									
								
							
							
						
						| @ -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); | ||||
| } | ||||
| @ -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(); | ||||
|   } | ||||
| } | ||||
| @ -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; | ||||
|   } | ||||
| } | ||||
| @ -1,161 +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 callList('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 call('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); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,215 +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 { | ||||
|     Map<AESKeySize, int> table = <AESKeySize, int>{ | ||||
|       AESKeySize.bits128: 128, | ||||
|       AESKeySize.bits192: 192, | ||||
|       AESKeySize.bits256: 256, | ||||
|     }; | ||||
|     return table[this]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 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)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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
									
								
							
							
						
						| @ -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, don’t 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) | ||||
| 
 | ||||
| @ -4,7 +4,7 @@ | ||||
| # This file should be version controlled and should not be manually edited. | ||||
| 
 | ||||
| version: | ||||
|   revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 | ||||
|   revision: cf4400006550b70f28e4b4af815151d1e74846c6 | ||||
|   channel: stable | ||||
| 
 | ||||
| project_type: plugin | ||||
							
								
								
									
										3
									
								
								native_crypto/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,3 @@ | ||||
| ## 0.0.1 | ||||
| 
 | ||||
| * TODO: Describe initial release. | ||||
							
								
								
									
										1
									
								
								native_crypto/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1 @@ | ||||
| TODO: Add your license here. | ||||
							
								
								
									
										18
									
								
								native_crypto/README.md
									
									
									
									
									
										Normal 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. | ||||
							
								
								
									
										4
									
								
								native_crypto/analysis_options.yaml
									
									
									
									
									
										Normal 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 | ||||
| @ -22,6 +22,7 @@ | ||||
| 
 | ||||
| # Flutter/Dart/Pub related | ||||
| **/doc/api/ | ||||
| **/ios/Flutter/.last_build_id | ||||
| .dart_tool/ | ||||
| .flutter-plugins | ||||
| .flutter-plugins-dependencies | ||||
| @ -33,5 +34,13 @@ | ||||
| # Web related | ||||
| lib/generated_plugin_registrant.dart | ||||
| 
 | ||||
| # Exceptions to above rules. | ||||
| !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | ||||
| # Symbolication related | ||||
| app.*.symbols | ||||
| 
 | ||||
| # Obfuscation related | ||||
| app.*.map.json | ||||
| 
 | ||||
| # Android Studio will place build artifacts here | ||||
| /android/app/debug | ||||
| /android/app/profile | ||||
| /android/app/release | ||||
| @ -4,7 +4,7 @@ | ||||
| # This file should be version controlled and should not be manually edited. | ||||
| 
 | ||||
| version: | ||||
|   revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 | ||||
|   revision: cf4400006550b70f28e4b4af815151d1e74846c6 | ||||
|   channel: stable | ||||
| 
 | ||||
| project_type: app | ||||
							
								
								
									
										29
									
								
								native_crypto/example/analysis_options.yaml
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										13
									
								
								native_crypto/example/android/.gitignore
									
									
									
									
										vendored
									
									
										Normal 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 | ||||
| @ -26,24 +26,28 @@ apply plugin: 'kotlin-android' | ||||
| apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" | ||||
| 
 | ||||
| android { | ||||
|     compileSdkVersion 28 | ||||
|     compileSdkVersion flutter.compileSdkVersion | ||||
| 
 | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| 
 | ||||
|     kotlinOptions { | ||||
|         jvmTarget = '1.8' | ||||
|     } | ||||
| 
 | ||||
|     sourceSets { | ||||
|         main.java.srcDirs += 'src/main/kotlin' | ||||
|     } | ||||
| 
 | ||||
|     lintOptions { | ||||
|         disable 'InvalidPackage' | ||||
|     } | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | ||||
|         applicationId "fr.pointcheval.native_crypto_example" | ||||
|         minSdkVersion 16 | ||||
|         targetSdkVersion 28 | ||||
|         minSdkVersion flutter.minSdkVersion | ||||
|         targetSdkVersion flutter.targetSdkVersion | ||||
|         versionCode flutterVersionCode.toInteger() | ||||
|         versionName flutterVersionName | ||||
|         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||||
|     } | ||||
| 
 | ||||
|     buildTypes { | ||||
| @ -61,7 +65,4 @@ flutter { | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | ||||
|     testImplementation 'junit:junit:4.12' | ||||
|     androidTestImplementation 'androidx.test:runner:1.1.1' | ||||
|     androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' | ||||
| } | ||||
| @ -1,21 +1,25 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="fr.pointcheval.native_crypto_example"> | ||||
|     <!-- io.flutter.app.FlutterApplication is an android.app.Application that | ||||
|          calls FlutterMain.startInitialization(this); in its onCreate method. | ||||
|          In most cases you can leave this as-is, but you if you want to provide | ||||
|          additional functionality it is fine to subclass or reimplement | ||||
|          FlutterApplication and put your custom class here. --> | ||||
|    <application | ||||
|         android:name="io.flutter.app.FlutterApplication" | ||||
|         android:label="native_crypto_example" | ||||
|         android:name="${applicationName}" | ||||
|         android:icon="@mipmap/ic_launcher"> | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:exported="true" | ||||
|             android:launchMode="singleTop" | ||||
|             android:theme="@style/LaunchTheme" | ||||
|             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||||
|             android:hardwareAccelerated="true" | ||||
|             android:windowSoftInputMode="adjustResize"> | ||||
|             <!-- Specifies an Android theme to apply to this Activity as soon as | ||||
|                  the Android process has started. This theme is visible to the user | ||||
|                  while the Flutter UI initializes. After that, this theme continues | ||||
|                  to determine the Window background behind the Flutter UI. --> | ||||
|             <meta-data | ||||
|               android:name="io.flutter.embedding.android.NormalTheme" | ||||
|               android:resource="@style/NormalTheme" | ||||
|               /> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN"/> | ||||
|                 <category android:name="android.intent.category.LAUNCHER"/> | ||||
| @ -0,0 +1,6 @@ | ||||
| package fr.pointcheval.native_crypto_example | ||||
| 
 | ||||
| import io.flutter.embedding.android.FlutterActivity | ||||
| 
 | ||||
| class MainActivity: FlutterActivity() { | ||||
| } | ||||
| @ -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> | ||||
| Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B | 
| Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B | 
| Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| @ -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> | ||||
| @ -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> | ||||
| @ -2,11 +2,11 @@ buildscript { | ||||
|     ext.kotlin_version = '1.3.50' | ||||
|     repositories { | ||||
|         google() | ||||
|         jcenter() | ||||
|         mavenCentral() | ||||
|     } | ||||
| 
 | ||||
|     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" | ||||
|     } | ||||
| } | ||||
| @ -14,7 +14,7 @@ buildscript { | ||||
| allprojects { | ||||
|     repositories { | ||||
|         google() | ||||
|         jcenter() | ||||
|         mavenCentral() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -1,4 +1,3 @@ | ||||
| org.gradle.jvmargs=-Xmx1536M | ||||
| android.enableR8=true | ||||
| android.useAndroidX=true | ||||
| android.enableJetifier=true | ||||
| @ -1,5 +1,6 @@ | ||||
| #Fri Jun 23 08:50:38 CEST 2017 | ||||
| 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 | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip | ||||
							
								
								
									
										11
									
								
								native_crypto/example/android/settings.gradle
									
									
									
									
									
										Normal 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" | ||||
| @ -1,3 +1,4 @@ | ||||
| **/dgph | ||||
| *.mode1v3 | ||||
| *.mode2v3 | ||||
| *.moved-aside | ||||
| @ -18,6 +19,7 @@ Flutter/App.framework | ||||
| Flutter/Flutter.framework | ||||
| Flutter/Flutter.podspec | ||||
| Flutter/Generated.xcconfig | ||||
| Flutter/ephemeral/ | ||||
| Flutter/app.flx | ||||
| Flutter/app.zip | ||||
| Flutter/flutter_assets/ | ||||
| @ -3,7 +3,7 @@ | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
|   <key>CFBundleDevelopmentRegion</key> | ||||
|   <string>$(DEVELOPMENT_LANGUAGE)</string> | ||||
|   <string>en</string> | ||||
|   <key>CFBundleExecutable</key> | ||||
|   <string>App</string> | ||||
|   <key>CFBundleIdentifier</key> | ||||
| @ -21,6 +21,6 @@ | ||||
|   <key>CFBundleVersion</key> | ||||
|   <string>1.0</string> | ||||
|   <key>MinimumOSVersion</key> | ||||
|   <string>8.0</string> | ||||
|   <string>9.0</string> | ||||
| </dict> | ||||
| </plist> | ||||
| @ -1,3 +1,2 @@ | ||||
| #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" | ||||
| #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" | ||||
| #include "Generated.xcconfig" | ||||
| @ -1,3 +1,2 @@ | ||||
| #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" | ||||
| #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" | ||||
| #include "Generated.xcconfig" | ||||
							
								
								
									
										41
									
								
								native_crypto/example/ios/Podfile
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										484
									
								
								native_crypto/example/ios/Runner.xcodeproj/project.pbxproj
									
									
									
									
									
										Normal 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 */; | ||||
| } | ||||
							
								
								
									
										7
									
								
								native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "self:"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
| @ -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> | ||||
| @ -1,6 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Scheme | ||||
|    LastUpgradeVersion = "1020" | ||||
|    LastUpgradeVersion = "1300" | ||||
|    version = "1.3"> | ||||
|    <BuildAction | ||||
|       parallelizeBuildables = "YES" | ||||
| @ -27,8 +27,6 @@ | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES"> | ||||
|       <Testables> | ||||
|       </Testables> | ||||
|       <MacroExpansion> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
| @ -38,8 +36,8 @@ | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </MacroExpansion> | ||||
|       <AdditionalOptions> | ||||
|       </AdditionalOptions> | ||||
|       <Testables> | ||||
|       </Testables> | ||||
|    </TestAction> | ||||
|    <LaunchAction | ||||
|       buildConfiguration = "Debug" | ||||
| @ -61,8 +59,6 @@ | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </BuildableProductRunnable> | ||||
|       <AdditionalOptions> | ||||
|       </AdditionalOptions> | ||||
|    </LaunchAction> | ||||
|    <ProfileAction | ||||
|       buildConfiguration = "Profile" | ||||
| @ -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> | ||||
| Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 564 B | 
| Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |