diff --git a/packages/native_crypto_android/android/build.gradle b/packages/native_crypto_android/android/build.gradle index 301735f..a30b841 100644 --- a/packages/native_crypto_android/android/build.gradle +++ b/packages/native_crypto_android/android/build.gradle @@ -47,4 +47,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.documentfile:documentfile:1.0.1' } diff --git a/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java b/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java index d48520e..8b10a22 100644 --- a/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java +++ b/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. // -- -// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// Autogenerated from Pigeon (v9.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package fr.pointcheval.native_crypto_android; @@ -26,15 +26,40 @@ import java.util.List; import java.util.Map; /** Generated class from Pigeon. */ -@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class GeneratedAndroidNativeCrypto { + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ + public static class FlutterError extends RuntimeException { + + /** The error code. */ + public final String code; + + /** The error details. Must be a datatype supported by the api codec. */ + public final Object details; + + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { + super(message); + this.code = code; + this.details = details; + } + } + @NonNull private static ArrayList wrapError(@NonNull Throwable exception) { ArrayList errorList = new ArrayList(3); - errorList.add(exception.toString()); - errorList.add(exception.getClass().getSimpleName()); - errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + if (exception instanceof FlutterError) { + FlutterError error = (FlutterError) exception; + errorList.add(error.code); + errorList.add(error.getMessage()); + errorList.add(error.details); + } else { + errorList.add(exception.toString()); + errorList.add(exception.getClass().getSimpleName()); + errorList.add( + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + } return errorList; } @@ -1339,16 +1364,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + HashRequest requestArg = (HashRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - HashRequest requestArg = (HashRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } HashResponse output = api.hash(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1366,16 +1388,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + HmacRequest requestArg = (HmacRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - HmacRequest requestArg = (HmacRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } HmacResponse output = api.hmac(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1393,16 +1412,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + GenerateSecureRandomRequest requestArg = (GenerateSecureRandomRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - GenerateSecureRandomRequest requestArg = (GenerateSecureRandomRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } GenerateSecureRandomResponse output = api.generateSecureRandom(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1420,16 +1436,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Pbkdf2Request requestArg = (Pbkdf2Request) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - Pbkdf2Request requestArg = (Pbkdf2Request) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } Pbkdf2Response output = api.pbkdf2(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1447,16 +1460,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + EncryptRequest requestArg = (EncryptRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - EncryptRequest requestArg = (EncryptRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } EncryptResponse output = api.encrypt(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1474,16 +1484,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + DecryptRequest requestArg = (DecryptRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - DecryptRequest requestArg = (DecryptRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } DecryptResponse output = api.decrypt(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1501,16 +1508,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + EncryptFileRequest requestArg = (EncryptFileRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - EncryptFileRequest requestArg = (EncryptFileRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } EncryptFileResponse output = api.encryptFile(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1528,16 +1532,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + DecryptFileRequest requestArg = (DecryptFileRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - DecryptFileRequest requestArg = (DecryptFileRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } DecryptFileResponse output = api.decryptFile(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1555,16 +1556,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + EncryptWithIVRequest requestArg = (EncryptWithIVRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - EncryptWithIVRequest requestArg = (EncryptWithIVRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } EncryptResponse output = api.encryptWithIV(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt new file mode 100644 index 0000000..3d1e12f --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt @@ -0,0 +1,118 @@ +package fr.pointcheval.native_crypto_android + +import android.content.Context +import fr.pointcheval.native_crypto_android.ciphers.AES +import fr.pointcheval.native_crypto_android.kdf.Pbkdf2 +import fr.pointcheval.native_crypto_android.utils.FileParameters +import fr.pointcheval.native_crypto_android.utils.HashAlgorithmParser +import java.security.SecureRandom + +class NativeCrypto(private val context: Context) : NativeCryptoAPI { + override fun hash(data: ByteArray, algorithm: HashAlgorithm): ByteArray? { + val md = HashAlgorithmParser.getMessageDigest(algorithm) + + return md.digest(data) + } + + override fun hmac(data: ByteArray, key: ByteArray, algorithm: HashAlgorithm): ByteArray? { + val mac = HashAlgorithmParser.getMac(algorithm) + val secretKey = javax.crypto.spec.SecretKeySpec(key, mac.algorithm) + mac.init(secretKey) + + return mac.doFinal(data) + } + + override fun generateSecureRandom(length: Long): ByteArray { + val bytes = ByteArray(length.toInt()) + SecureRandom.getInstanceStrong().nextBytes(bytes) + + return bytes + } + + override fun pbkdf2( + password: ByteArray, + salt: ByteArray, + length: Long, + iterations: Long, + algorithm: HashAlgorithm + ): ByteArray? { + val pbkdf2 = Pbkdf2(length.toInt(), iterations.toInt(), algorithm) + pbkdf2.init(password, salt) + + return pbkdf2.derive() + } + + override fun encrypt( + plainText: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): ByteArray { + // For now, only AES is supported + val aes = AES() + + return aes.encrypt(plainText, key, null) + } + + override fun encryptWithIV( + plainText: ByteArray, + iv: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): ByteArray { + // For now, only AES is supported + val aes = AES() + + return aes.encrypt(plainText, key, iv) + } + + override fun decrypt( + cipherText: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): ByteArray { + // For now, only AES is supported + val aes = AES() + + return aes.decrypt(cipherText, key) + } + + override fun encryptFile( + plainTextPath: String, + cipherTextPath: String, + key: ByteArray, + algorithm: CipherAlgorithm + ): Boolean { + // For now, only AES is supported + val aes = AES() + val params = FileParameters(context, plainTextPath, cipherTextPath) + + return aes.encryptFile(params, key, null) + } + + override fun encryptFileWithIV( + plainTextPath: String, + cipherTextPath: String, + iv: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): Boolean { + // For now, only AES is supported + val aes = AES() + val params = FileParameters(context, plainTextPath, cipherTextPath) + + return aes.encryptFile(params, key, iv) + } + + override fun decryptFile( + cipherTextPath: String, + plainTextPath: String, + key: ByteArray, + algorithm: CipherAlgorithm + ): Boolean { + // For now, only AES is supported + val aes = AES() + val params = FileParameters(context, plainTextPath, cipherTextPath) + + return aes.decryptFile(params, key) + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt index 36cd412..4c8b235 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt @@ -1,160 +1,19 @@ package fr.pointcheval.native_crypto_android -import androidx.annotation.NonNull -import fr.pointcheval.native_crypto_android.interfaces.Cipher -import fr.pointcheval.native_crypto_android.kdf.Pbkdf2 -import fr.pointcheval.native_crypto_android.keys.SecretKey -import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm -import fr.pointcheval.native_crypto_android.utils.Constants -import fr.pointcheval.native_crypto_android.utils.HashAlgorithm -import fr.pointcheval.native_crypto_android.utils.Task 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 java.util.* /** NativeCryptoAndroidPlugin */ -class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel: MethodChannel - private val name = "plugins.hugop.cl/native_crypto" +class NativeCryptoAndroidPlugin : FlutterPlugin { + private var nativeCrypto: NativeCrypto? = null - private var cipherInstance: Cipher? = null - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, name) - channel.setMethodCallHandler(this) + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + val context = flutterPluginBinding.applicationContext + nativeCrypto = NativeCrypto(context) + NativeCryptoAPI.setUp(flutterPluginBinding.binaryMessenger, nativeCrypto) } - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - lateinit var methodCallTask: Task<*> - - when (call.method) { - "digest" -> methodCallTask = handleDigest(call.arguments()) - "generateSecretKey" -> methodCallTask = handleGenerateSecretKey(call.arguments()) - "pbkdf2" -> methodCallTask = handlePbkdf2(call.arguments()) - "encryptAsList" -> methodCallTask = handleEncryptAsList(call.arguments()) - "decryptAsList" -> methodCallTask = handleDecryptAsList(call.arguments()) - "encrypt" -> methodCallTask = handleCrypt(call.arguments(), true) - "decrypt" -> methodCallTask = handleCrypt(call.arguments(), false) - else -> result.notImplemented() - } - - methodCallTask.call() - - methodCallTask.finalize { task -> - if (task.isSuccessful()) { - result.success(task.getResult()) - } else { - val exception: Exception = task.getException() - val message = exception.message - result.error("native_crypto", message, null) - } - } - } - - private fun handleDigest(arguments: Map?): Task { - return Task { - val data: ByteArray = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - HashAlgorithm.digest(data, algorithm) - } - } - - private fun handleGenerateSecretKey(arguments: Map?): Task { - return Task { - val bitsCount: Int = Objects.requireNonNull(arguments?.get(Constants.BITS_COUNT)) as Int - SecretKey.fromSecureRandom(bitsCount).bytes - } - } - - private fun handlePbkdf2(arguments: Map?): Task { - return Task { - val password: String = - Objects.requireNonNull(arguments?.get(Constants.PASSWORD)) as String - val salt: String = Objects.requireNonNull(arguments?.get(Constants.SALT)) as String - val keyBytesCount: Int = - Objects.requireNonNull(arguments?.get(Constants.KEY_BYTES_COUNT)) as Int - val iterations: Int = - Objects.requireNonNull(arguments?.get(Constants.ITERATIONS)) as Int - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val pbkdf2: Pbkdf2 = Pbkdf2(keyBytesCount, iterations, HashAlgorithm.valueOf(algorithm)) - pbkdf2.init(password, salt) - - pbkdf2.derive().bytes - } - } - - private fun lazyLoadCipher(cipherAlgorithm: CipherAlgorithm) { - if (cipherInstance == null) { - cipherInstance = cipherAlgorithm.getCipher() - } else { - if (cipherInstance!!.algorithm != cipherAlgorithm) { - cipherInstance = cipherAlgorithm.getCipher() - } - } - } - - private fun handleEncryptAsList(arguments: Map?): Task> { - return Task { - val data: ByteArray = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray - val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) - lazyLoadCipher(cipherAlgorithm) - - cipherInstance!!.encryptAsList(data, key) - } - } - - private fun handleDecryptAsList(arguments: Map?): Task { - return Task { - val data: List = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as List - val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) - lazyLoadCipher(cipherAlgorithm) - - cipherInstance!!.decryptAsList(data, key) - } - } - - // **EN**Crypt and **DE**Crypt - private fun handleCrypt(arguments: Map?, forEncryption: Boolean): Task { - return Task { - val data: ByteArray = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray - val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) - lazyLoadCipher(cipherAlgorithm) - - if (forEncryption) { - cipherInstance!!.encrypt(data, key) - } else { - cipherInstance!!.decrypt(data, key) - } - } + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + NativeCryptoAPI.setUp(binding.binaryMessenger, null) + nativeCrypto = null } } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt new file mode 100644 index 0000000..0383075 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt @@ -0,0 +1,300 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.2.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package fr.pointcheval.native_crypto_android + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + if (exception is FlutterError) { + return listOf( + exception.code, + exception.message, + exception.details + ) + } else { + return listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class HashAlgorithm(val raw: Int) { + SHA256(0), + SHA384(1), + SHA512(2); + + companion object { + fun ofRaw(raw: Int): HashAlgorithm? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class CipherAlgorithm(val raw: Int) { + AES(0); + + companion object { + fun ofRaw(raw: Int): CipherAlgorithm? { + return values().firstOrNull { it.raw == raw } + } + } +} +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface NativeCryptoAPI { + fun hash(data: ByteArray, algorithm: HashAlgorithm): ByteArray? + fun hmac(data: ByteArray, key: ByteArray, algorithm: HashAlgorithm): ByteArray? + fun generateSecureRandom(length: Long): ByteArray? + fun pbkdf2(password: ByteArray, salt: ByteArray, length: Long, iterations: Long, algorithm: HashAlgorithm): ByteArray? + fun encrypt(plainText: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): ByteArray? + fun encryptWithIV(plainText: ByteArray, iv: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): ByteArray? + fun decrypt(cipherText: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): ByteArray? + fun encryptFile(plainTextPath: String, cipherTextPath: String, key: ByteArray, algorithm: CipherAlgorithm): Boolean? + fun encryptFileWithIV(plainTextPath: String, cipherTextPath: String, iv: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): Boolean? + fun decryptFile(cipherTextPath: String, plainTextPath: String, key: ByteArray, algorithm: CipherAlgorithm): Boolean? + + companion object { + /** The codec used by NativeCryptoAPI. */ + val codec: MessageCodec by lazy { + StandardMessageCodec() + } + /** Sets up an instance of `NativeCryptoAPI` to handle messages through the `binaryMessenger`. */ + @Suppress("UNCHECKED_CAST") + fun setUp(binaryMessenger: BinaryMessenger, api: NativeCryptoAPI?) { + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hash", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val dataArg = args[0] as ByteArray + val algorithmArg = HashAlgorithm.ofRaw(args[1] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.hash(dataArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hmac", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val dataArg = args[0] as ByteArray + val keyArg = args[1] as ByteArray + val algorithmArg = HashAlgorithm.ofRaw(args[2] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.hmac(dataArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val lengthArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + wrapped = listOf(api.generateSecureRandom(lengthArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.pbkdf2", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val passwordArg = args[0] as ByteArray + val saltArg = args[1] as ByteArray + val lengthArg = args[2].let { if (it is Int) it.toLong() else it as Long } + val iterationsArg = args[3].let { if (it is Int) it.toLong() else it as Long } + val algorithmArg = HashAlgorithm.ofRaw(args[4] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.pbkdf2(passwordArg, saltArg, lengthArg, iterationsArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encrypt", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextArg = args[0] as ByteArray + val keyArg = args[1] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[2] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encrypt(plainTextArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextArg = args[0] as ByteArray + val ivArg = args[1] as ByteArray + val keyArg = args[2] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[3] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encryptWithIV(plainTextArg, ivArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decrypt", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val cipherTextArg = args[0] as ByteArray + val keyArg = args[1] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[2] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.decrypt(cipherTextArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextPathArg = args[0] as String + val cipherTextPathArg = args[1] as String + val keyArg = args[2] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[3] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encryptFile(plainTextPathArg, cipherTextPathArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextPathArg = args[0] as String + val cipherTextPathArg = args[1] as String + val ivArg = args[2] as ByteArray + val keyArg = args[3] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[4] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encryptFileWithIV(plainTextPathArg, cipherTextPathArg, ivArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decryptFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val cipherTextPathArg = args[0] as String + val plainTextPathArg = args[1] as String + val keyArg = args[2] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[3] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.decryptFile(cipherTextPathArg, plainTextPathArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt index 0b4c86c..94e0ce8 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt @@ -1,55 +1,146 @@ package fr.pointcheval.native_crypto_android.ciphers import fr.pointcheval.native_crypto_android.interfaces.Cipher -import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm -import javax.crypto.SecretKey +import fr.pointcheval.native_crypto_android.utils.FileParameters +import javax.crypto.CipherOutputStream import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec class AES : Cipher { - override val algorithm: CipherAlgorithm - get() = CipherAlgorithm.aes + private var cipherInstance: javax.crypto.Cipher? = null - var cipherInstance: javax.crypto.Cipher? = null; - - fun lazyLoadCipher() { + private fun lazyLoadCipher() { if (cipherInstance == null) { cipherInstance = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") } } - // native.crypto cipherText representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] - // javax.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] - override fun encrypt(data: ByteArray, key: ByteArray): ByteArray { - val list : List = encryptAsList(data, key) - return list.first().plus(list.last()) - } - // native.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] - // javax.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] - override fun encryptAsList(data: ByteArray, key: ByteArray): List { + // native.crypto cipherText representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] + override fun encrypt(data: ByteArray, key: ByteArray, predefinedIV: ByteArray?): ByteArray { + // Initialize secret key spec val sk = SecretKeySpec(key, "AES") + + // Initialize cipher (if not already done) lazyLoadCipher() - cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + + // If predefinedIV is not null, use it + if (predefinedIV != null && predefinedIV.isNotEmpty()) { + // Here we use the predefinedIV as the nonce (12 bytes) + // And we set the tag length to 16 bytes (128 bits) + val gcmParameterSpec = GCMParameterSpec(16*8, predefinedIV) + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk, gcmParameterSpec) + } else { + // If predefinedIV is null, we generate a new one + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + } + + // Encrypt data val bytes: ByteArray = cipherInstance!!.doFinal(data) val iv: ByteArray = cipherInstance!!.iv - return listOf(iv, bytes) + + return iv.plus(bytes) + } + + override fun encryptFile(fileParameters: FileParameters, key: ByteArray, predefinedIV: ByteArray?): Boolean { + // Initialize secret key spec + val sk = SecretKeySpec(key, "AES") + + // Initialize cipher (if not already done) + lazyLoadCipher() + + // If predefinedIV is not null, use it + if (predefinedIV != null && predefinedIV.isNotEmpty()) { + // Here we use the predefinedIV as the nonce (12 bytes) + // And we set the tag length to 16 bytes (128 bits) + val gcmParameterSpec = GCMParameterSpec(16*8, predefinedIV) + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk, gcmParameterSpec) + } else { + // If predefinedIV is null, we generate a new one + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + } + + var len: Int? + val buffer = ByteArray(8192) + val inputFile = fileParameters.getFileInputStream() + val outputFile = fileParameters.getFileOutputStream() + val iv: ByteArray? = cipherInstance!!.iv + + outputFile?.write(iv) + outputFile?.flush() + + val encryptedStream = CipherOutputStream(outputFile!!, cipherInstance) + while(true) { + len = inputFile?.read(buffer) + if (len != null && len > 0) { + encryptedStream.write(buffer,0,len) + } else { + break + } + } + encryptedStream.flush() + encryptedStream.close() + inputFile?.close() + outputFile.close() + + return fileParameters.outputExists() } override fun decrypt(data: ByteArray, key: ByteArray): ByteArray { + // Extract the IV from the cipherText val iv: ByteArray = data.take(12).toByteArray() val payload: ByteArray = data.drop(12).toByteArray() - return decryptAsList(listOf(iv, payload), key) - } - override fun decryptAsList(data: List, key: ByteArray): ByteArray { + // Initialize secret key spec val sk = SecretKeySpec(key, "AES") - val payload: ByteArray = data.last() - val iv: ByteArray = data.first() - val gcmSpec = GCMParameterSpec(16 * 8, iv) + + // Initialize GCMParameterSpec + val gcmParameterSpec = GCMParameterSpec(16 * 8, iv) + + // Initialize cipher (if not already done) lazyLoadCipher() - cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmSpec) + cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec) + + // Decrypt data return cipherInstance!!.doFinal(payload) } + + override fun decryptFile(fileParameters: FileParameters, key: ByteArray): Boolean { + val iv = ByteArray(12) + val inputFile = fileParameters.getFileInputStream() ?: throw Exception("Error while reading IV") + + // Read the first 12 bytes from the file + for (i in 0 until 12) { + iv[i] = inputFile.read().toByte() + } + + // Initialize secret key spec + val sk = SecretKeySpec(key, "AES") + + // Initialize GCMParameterSpec + val gcmParameterSpec = GCMParameterSpec(16 * 8, iv) + + // Initialize cipher (if not already done) + lazyLoadCipher() + + cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec) + + var len: Int? + val buffer = ByteArray(8192) + val outputFile = fileParameters.getFileOutputStream() + val decryptedStream = CipherOutputStream(outputFile!!, cipherInstance) + while (true) { + len = inputFile.read(buffer) + if(len > 0){ + decryptedStream.write(buffer,0, len) + } else { + break + } + } + decryptedStream.flush() + decryptedStream.close() + inputFile.close() + + return fileParameters.outputExists() + } } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt index 5893d25..6d8e63f 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt @@ -1,12 +1,10 @@ package fr.pointcheval.native_crypto_android.interfaces -import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm +import fr.pointcheval.native_crypto_android.utils.FileParameters interface Cipher { - val algorithm: CipherAlgorithm - - fun encrypt(data: ByteArray, key: ByteArray): ByteArray + fun encrypt(data: ByteArray, key: ByteArray, predefinedIV: ByteArray?): ByteArray fun decrypt(data: ByteArray, key: ByteArray): ByteArray - fun encryptAsList(data: ByteArray, key: ByteArray): List - fun decryptAsList(data: List, key: ByteArray): ByteArray + fun encryptFile(fileParameters: FileParameters, key: ByteArray, predefinedIV: ByteArray?): Boolean + fun decryptFile(fileParameters: FileParameters, key: ByteArray): Boolean } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt deleted file mode 100644 index 4df212b..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt +++ /dev/null @@ -1,5 +0,0 @@ -package fr.pointcheval.native_crypto_android.interfaces - -interface Key { - val bytes: ByteArray -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt index 6bd9216..17db04e 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt @@ -1,10 +1,5 @@ package fr.pointcheval.native_crypto_android.interfaces -import fr.pointcheval.native_crypto_android.keys.SecretKey -import fr.pointcheval.native_crypto_android.utils.KdfAlgorithm - interface KeyDerivation { - val algorithm: KdfAlgorithm - - fun derive(): SecretKey + fun derive(): ByteArray? } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt index 17d8c5e..0daa8ae 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt @@ -1,39 +1,43 @@ package fr.pointcheval.native_crypto_android.kdf +import fr.pointcheval.native_crypto_android.HashAlgorithm import fr.pointcheval.native_crypto_android.interfaces.KeyDerivation -import fr.pointcheval.native_crypto_android.keys.SecretKey -import fr.pointcheval.native_crypto_android.utils.HashAlgorithm -import fr.pointcheval.native_crypto_android.utils.KdfAlgorithm +import fr.pointcheval.native_crypto_android.utils.HashAlgorithmParser import javax.crypto.SecretKeyFactory import javax.crypto.spec.PBEKeySpec class Pbkdf2( - private val keyBytesCount: Int, private val iterations: Int, - private val hash: HashAlgorithm = HashAlgorithm.sha256 + private val length: Int, private val iterations: Int, + private val hashAlgorithm: HashAlgorithm ) : KeyDerivation { - private var password: String? = null - private var salt: String? = null + private var password: CharArray? = null + private var salt: ByteArray? = null - fun init(password: String, salt: String) { - this.password = password + fun init(password: ByteArray, salt: ByteArray) { + // Transform the password to a char array + val passwordCharArray = CharArray(password.size) + for (i in password.indices) { + passwordCharArray[i] = password[i].toInt().toChar() + } + + this.password = passwordCharArray this.salt = salt } - override val algorithm: KdfAlgorithm - get() = KdfAlgorithm.pbkdf2 - - override fun derive(): SecretKey { + override fun derive(): ByteArray? { if (password == null || salt == null) { throw Exception("Password and Salt must be initialized.") } val spec = PBEKeySpec( - password!!.toCharArray(), - salt!!.toByteArray(), + password!!, + salt!!, iterations, - keyBytesCount * 8 + length * 8 ) - val skf: SecretKeyFactory = SecretKeyFactory.getInstance(hash.pbkdf2String()) - return SecretKey(skf.generateSecret(spec).encoded) + val skf: SecretKeyFactory = + SecretKeyFactory.getInstance(HashAlgorithmParser.getPbkdf2String(hashAlgorithm)) + + return skf.generateSecret(spec).encoded } } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt deleted file mode 100644 index 07e9833..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.pointcheval.native_crypto_android.keys - -import fr.pointcheval.native_crypto_android.interfaces.Key -import java.security.SecureRandom - -class SecretKey(override val bytes: ByteArray) : Key { - companion object { - fun fromSecureRandom(bitsCount: Int): SecretKey { - val bytes = ByteArray(bitsCount / 8) - SecureRandom.getInstanceStrong().nextBytes(bytes) - return SecretKey(bytes) - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt deleted file mode 100644 index a95185f..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -import fr.pointcheval.native_crypto_android.ciphers.AES -import fr.pointcheval.native_crypto_android.interfaces.Cipher - -enum class CipherAlgorithm { - aes; - - fun getCipher(): Cipher { - return when (this) { - aes -> AES() - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt deleted file mode 100644 index 1cb359a..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -object Constants { - const val ALGORITHM = "algorithm" - const val BITS_COUNT = "bitsCount" - const val DATA = "data" - const val PASSWORD = "password" - const val SALT = "salt" - const val KEY = "key" - const val KEY_BYTES_COUNT = "keyBytesCount" - const val ITERATIONS = "iterations" - const val EPHEMERAL_PRIVATE_KEY = "ephemeralPrivateKey" - const val OTHER_PUBLIC_KEY = "otherPublicKey" -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt new file mode 100644 index 0000000..44207dc --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt @@ -0,0 +1,71 @@ +package fr.pointcheval.native_crypto_android.utils + +import android.content.Context +import android.content.res.Resources +import android.net.Uri +import androidx.documentfile.provider.DocumentFile +import java.io.* + +class FileParameters(ctx: Context, input: String, output: String) { + private var context: Context + + private var inputPath: String + private var outputPath: String + + init { + this.context = ctx + this.inputPath = input + this.outputPath = output + } + + private fun getUri(): Uri? { + val persistedUriPermissions = context.contentResolver.persistedUriPermissions + if (persistedUriPermissions.size > 0) { + val uriPermission = persistedUriPermissions[0] + return uriPermission.uri + } + return null + } + + private fun getDocumentFileByPath(path: String): DocumentFile { + var doc = DocumentFile.fromTreeUri(context, getUri()!!) + val parts = path.split("/") + for (i in parts.indices) { + val nextFile = doc?.findFile(parts[i]) + if(nextFile != null){ + doc = nextFile + } + } + if (doc != null){ + return doc + } else { + throw Resources.NotFoundException("File not found") + } + } + + fun getFileOutputStream(): OutputStream? { + val path = outputPath + return try{ + FileOutputStream(path) + } catch(e: IOException){ + val documentFile: DocumentFile = this.getDocumentFileByPath(path) + val documentUri = documentFile.uri + context.contentResolver.openOutputStream(documentUri) + } + } + + fun getFileInputStream(): InputStream? { + val path = inputPath + return try{ + FileInputStream(path) + } catch(e: IOException){ + val documentFile: DocumentFile = this.getDocumentFileByPath(path) + val documentUri = documentFile.uri + context.contentResolver.openInputStream(documentUri) + } + } + + fun outputExists(): Boolean { + return File(outputPath).exists() + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt deleted file mode 100644 index 904f7ce..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt +++ /dev/null @@ -1,50 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -import java.security.MessageDigest - -@Suppress("EnumEntryName") -enum class HashAlgorithm(val bitsCount: Int) { - sha256(256), - sha384(384), - sha512(512); - - fun messageDigestString(): String { - return when (this) { - sha256 -> "SHA-256" - sha384 -> "SHA-384" - sha512 -> "SHA-512" - } - } - - fun hmacString(): String { - return when (this) { - sha256 -> "HmacSHA256" - sha384 -> "HmacSHA384" - sha512 -> "HmacSHA512" - } - } - - fun pbkdf2String(): String { - return when (this) { - sha256 -> "PBKDF2WithHmacSHA256" - sha384 -> "PBKDF2WithHmacSHA384" - sha512 -> "PBKDF2WithHmacSHA512" - } - } - - fun digest(data: ByteArray): ByteArray { - val md = MessageDigest.getInstance(messageDigestString()) - return md.digest(data) - } - - companion object { - fun digest(data: ByteArray, algorithm: String): ByteArray { - for (h in values()) { - if (h.name == algorithm) { - return h.digest(data) - } - } - throw Exception("Unknown HashAlgorithm: $algorithm") - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt new file mode 100644 index 0000000..0114acb --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt @@ -0,0 +1,31 @@ +package fr.pointcheval.native_crypto_android.utils + +import fr.pointcheval.native_crypto_android.HashAlgorithm +import java.security.MessageDigest +import javax.crypto.Mac + +object HashAlgorithmParser { + fun getMessageDigest(algorithm: HashAlgorithm): MessageDigest { + return when (algorithm) { + HashAlgorithm.SHA256 -> MessageDigest.getInstance("SHA-256") + HashAlgorithm.SHA384 -> MessageDigest.getInstance("SHA-384") + HashAlgorithm.SHA512 -> MessageDigest.getInstance("SHA-512") + } + } + + fun getMac(algorithm: HashAlgorithm): Mac { + return when (algorithm) { + HashAlgorithm.SHA256 -> Mac.getInstance("HmacSHA256") + HashAlgorithm.SHA384 -> Mac.getInstance("HmacSHA384") + HashAlgorithm.SHA512 -> Mac.getInstance("HmacSHA512") + } + } + + fun getPbkdf2String(algorithm: HashAlgorithm): String { + return when (algorithm) { + HashAlgorithm.SHA256 -> "PBKDF2WithHmacSHA256" + HashAlgorithm.SHA384 -> "PBKDF2WithHmacSHA384" + HashAlgorithm.SHA512 -> "PBKDF2WithHmacSHA512" + } + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt deleted file mode 100644 index 4ef9f69..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt +++ /dev/null @@ -1,5 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -enum class KdfAlgorithm { - pbkdf2 -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt deleted file mode 100644 index d4832de..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt +++ /dev/null @@ -1,44 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -class Task(private var task: () -> T) { - - private var successful = false - private var result: T? = null - private var exception: Exception? = null - - fun isSuccessful(): Boolean { - return successful - } - - fun getResult(): T { - if (successful && result != null) { - return result!! - } else { - throw Exception("No result found!") - } - } - - fun getException(): Exception { - if (exception != null) { - return exception!! - } else { - throw Exception("No exception found!") - } - } - - fun call() { - try { - result = task() - exception = null - successful = true - } catch (e: Exception) { - exception = e - result = null - successful = false - } - } - - fun finalize(callback: (task: Task) -> Unit) { - callback(this) - } -} \ No newline at end of file