feat(android): use kotlin pigeon generator
This commit is contained in:
parent
f570ed076a
commit
560f5b4942
@ -47,4 +47,5 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
}
|
||||
|
@ -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<Object> wrapError(@NonNull Throwable exception) {
|
||||
ArrayList<Object> errorList = new ArrayList<Object>(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<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
HashRequest requestArg = (HashRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1366,16 +1388,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
HmacRequest requestArg = (HmacRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1393,16 +1412,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
GenerateSecureRandomRequest requestArg = (GenerateSecureRandomRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1420,16 +1436,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Pbkdf2Request requestArg = (Pbkdf2Request) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1447,16 +1460,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
EncryptRequest requestArg = (EncryptRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1474,16 +1484,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
DecryptRequest requestArg = (DecryptRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1501,16 +1508,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
EncryptFileRequest requestArg = (EncryptFileRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1528,16 +1532,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
DecryptFileRequest requestArg = (DecryptFileRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
@ -1555,16 +1556,13 @@ public class GeneratedAndroidNativeCrypto {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
EncryptWithIVRequest requestArg = (EncryptWithIVRequest) args.get(0);
|
||||
try {
|
||||
ArrayList<Object> args = (ArrayList<Object>) 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<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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<String, Any>?): Task<ByteArray> {
|
||||
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<String, Any>?): Task<ByteArray> {
|
||||
return Task {
|
||||
val bitsCount: Int = Objects.requireNonNull(arguments?.get(Constants.BITS_COUNT)) as Int
|
||||
SecretKey.fromSecureRandom(bitsCount).bytes
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePbkdf2(arguments: Map<String, Any>?): Task<ByteArray> {
|
||||
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<String, Any>?): Task<List<ByteArray>> {
|
||||
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<String, Any>?): Task<ByteArray> {
|
||||
return Task {
|
||||
val data: List<ByteArray> =
|
||||
Objects.requireNonNull(arguments?.get(Constants.DATA)) as List<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!!.decryptAsList(data, key)
|
||||
}
|
||||
}
|
||||
|
||||
// **EN**Crypt and **DE**Crypt
|
||||
private fun handleCrypt(arguments: Map<String, Any>?, forEncryption: Boolean): Task<ByteArray> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<Any?> {
|
||||
return listOf(result)
|
||||
}
|
||||
|
||||
private fun wrapError(exception: Throwable): List<Any?> {
|
||||
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<Any?> 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<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hash", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val dataArg = args[0] as ByteArray
|
||||
val algorithmArg = HashAlgorithm.ofRaw(args[1] as Int)!!
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.hash(dataArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hmac", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val dataArg = args[0] as ByteArray
|
||||
val keyArg = args[1] as ByteArray
|
||||
val algorithmArg = HashAlgorithm.ofRaw(args[2] as Int)!!
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.hmac(dataArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val lengthArg = args[0].let { if (it is Int) it.toLong() else it as Long }
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.generateSecureRandom(lengthArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.pbkdf2", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
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<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.pbkdf2(passwordArg, saltArg, lengthArg, iterationsArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encrypt", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val plainTextArg = args[0] as ByteArray
|
||||
val keyArg = args[1] as ByteArray
|
||||
val algorithmArg = CipherAlgorithm.ofRaw(args[2] as Int)!!
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.encrypt(plainTextArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
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<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.encryptWithIV(plainTextArg, ivArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decrypt", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val cipherTextArg = args[0] as ByteArray
|
||||
val keyArg = args[1] as ByteArray
|
||||
val algorithmArg = CipherAlgorithm.ofRaw(args[2] as Int)!!
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.decrypt(cipherTextArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFile", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
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<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.encryptFile(plainTextPathArg, cipherTextPathArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
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<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.encryptFileWithIV(plainTextPathArg, cipherTextPathArg, ivArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decryptFile", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
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<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.decryptFile(cipherTextPathArg, plainTextPathArg, keyArg, algorithmArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ByteArray> = 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<ByteArray> {
|
||||
// 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<ByteArray>, 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()
|
||||
}
|
||||
}
|
@ -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<ByteArray>
|
||||
fun decryptAsList(data: List<ByteArray>, key: ByteArray): ByteArray
|
||||
fun encryptFile(fileParameters: FileParameters, key: ByteArray, predefinedIV: ByteArray?): Boolean
|
||||
fun decryptFile(fileParameters: FileParameters, key: ByteArray): Boolean
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package fr.pointcheval.native_crypto_android.interfaces
|
||||
|
||||
interface Key {
|
||||
val bytes: ByteArray
|
||||
}
|
@ -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?
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package fr.pointcheval.native_crypto_android.utils
|
||||
|
||||
enum class KdfAlgorithm {
|
||||
pbkdf2
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package fr.pointcheval.native_crypto_android.utils
|
||||
|
||||
class Task<T>(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<T>) -> Unit) {
|
||||
callback(this)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user