diff --git a/packages/native_crypto_android/android/build.gradle b/packages/native_crypto_android/android/build.gradle index ec447a5..301735f 100644 --- a/packages/native_crypto_android/android/build.gradle +++ b/packages/native_crypto_android/android/build.gradle @@ -2,14 +2,14 @@ group 'fr.pointcheval.native_crypto_android' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Cipher.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Cipher.kt deleted file mode 100644 index 78b1a79..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Cipher.kt +++ /dev/null @@ -1,6 +0,0 @@ -package fr.pointcheval.native_crypto_android - -abstract class Cipher { - abstract fun encrypt(data: ByteArray, key: ByteArray) : ByteArray?; - abstract fun decrypt(data: ByteArray, key: ByteArray) : ByteArray?; -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Hash.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Hash.kt deleted file mode 100644 index dd9c95e..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Hash.kt +++ /dev/null @@ -1,25 +0,0 @@ -package fr.pointcheval.native_crypto_android - -import java.security.MessageDigest - -object Hash { - fun digest(data: ByteArray?, algorithm: HashAlgorithm): ByteArray { - val func : String = when (algorithm) { - HashAlgorithm.SHA256 -> "SHA-256" - HashAlgorithm.SHA384 -> "SHA-384" - HashAlgorithm.SHA512 -> "SHA-512" - } - val md = MessageDigest.getInstance(func) - return md.digest(data) - } - - fun digest(data: ByteArray?, algorithm: String): ByteArray { - val func : HashAlgorithm = when (algorithm) { - "sha256" -> HashAlgorithm.SHA256 - "sha384" -> HashAlgorithm.SHA384 - "sha512" -> HashAlgorithm.SHA512 - else -> HashAlgorithm.SHA256 - } - return digest(data, func) - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/HashAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/HashAlgorithm.kt deleted file mode 100644 index f820bae..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/HashAlgorithm.kt +++ /dev/null @@ -1,15 +0,0 @@ -package fr.pointcheval.native_crypto_android - -enum class HashAlgorithm(val length : Int) { - SHA256(256), - SHA384(384), - SHA512(512); - - fun hmac(): String { - return when (this) { - HashAlgorithm.SHA256 -> "HmacSHA256" - HashAlgorithm.SHA384 -> "HmacSHA384" - HashAlgorithm.SHA512 -> "HmacSHA512" - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Key.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Key.kt deleted file mode 100644 index d099a89..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Key.kt +++ /dev/null @@ -1,24 +0,0 @@ -package fr.pointcheval.native_crypto_android - -import java.security.SecureRandom -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.PBEKeySpec - -object Key { - fun fromSecureRandom(bitsCount: Int) : ByteArray { - val bytes = ByteArray(bitsCount / 8) - SecureRandom.getInstanceStrong().nextBytes(bytes) - return bytes - } - - fun fromPBKDF2(password: String, salt: String, keyBytesCount: Int, iterations: Int, algorithm: String): ByteArray { - val availableHashAlgorithm: Map = mapOf( - "sha256" to "PBKDF2WithHmacSHA256", - "sha384" to "PBKDF2withHmacSHA384", - "sha512" to "PBKDF2withHmacSHA512" - ) - val spec = PBEKeySpec(password.toCharArray(), salt.toByteArray(), iterations, keyBytesCount * 8) - val skf: SecretKeyFactory = SecretKeyFactory.getInstance(availableHashAlgorithm[algorithm]) - 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/NativeCryptoAndroidPlugin.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt index e9c5219..2e1b5e3 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,109 +1,127 @@ package fr.pointcheval.native_crypto_android import androidx.annotation.NonNull -import fr.pointcheval.native_crypto_android.ciphers.AESCipher - +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 +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" - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.flutterEngine.dartExecutor, "plugins.hugop.cl/native_crypto") - channel.setMethodCallHandler(this) - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - when (call.method) { - "digest" -> { - if (!call.hasArgument("data")) result.error("DATA_NULL", null, null); - if (!call.hasArgument("algorithm")) result.error("ALGORITHM_NULL", null, null); - - val data : ByteArray = call.argument("data")!! - val algorithm : String = call.argument("algorithm")!! - - result.success(Hash.digest(data, algorithm)) - } - "generateSecretKey" -> { - if (!call.hasArgument("bitsCount")) result.error("SIZE_NULL", null, null); - - val bitsCount : Int = call.argument("bitsCount")!! - - result.success(Key.fromSecureRandom(bitsCount)) - } - "generateKeyPair" -> { - result.notImplemented() - } - "pbkdf2" -> { - if (!call.hasArgument("password")) result.error("PASSWORD_NULL", null, null); - if (!call.hasArgument("salt")) result.error("SALT_NULL", null, null); - if (!call.hasArgument("keyBytesCount")) result.error("SIZE_NULL", null, null); - if (!call.hasArgument("iterations")) result.error("ITERATIONS_NULL", null, null); - if (!call.hasArgument("algorithm")) result.error("ALGORITHM_NULL", null, null); - - val password : String = call.argument("password")!! - val salt : String = call.argument("salt")!! - val keyBytesCount : Int = call.argument("keyBytesCount")!! - val iterations : Int = call.argument("iterations")!! - val algorithm : String = call.argument("algorithm")!! - - result.success(Key.fromPBKDF2(password, salt, keyBytesCount, iterations, algorithm)) - } - "encrypt" -> { - if (!call.hasArgument("data")) result.error("DATA_NULL", null, null); - if (!call.hasArgument("key")) result.error("KEY_NULL", null, null); - if (!call.hasArgument("algorithm")) result.error("ALGORITHM_NULL", null, null); - - val data : ByteArray = call.argument("data")!! - val key : ByteArray = call.argument("key")!! - val algorithm : String = call.argument("algorithm")!! - - if (algorithm == "aes") { - result.success(AESCipher().encrypt(data, key)) - } - } - "decrypt" -> { - if (!call.hasArgument("data")) result.error("DATA_NULL", null, null); - if (!call.hasArgument("key")) result.error("KEY_NULL", null, null); - if (!call.hasArgument("algorithm")) result.error("ALGORITHM_NULL", null, null); - - val data : ByteArray = call.argument("data")!! - val key : ByteArray = call.argument("key")!! - val algorithm : String = call.argument("algorithm")!! - - if (algorithm == "aes") { - result.success(AESCipher().decrypt(data, key)) - } - } - "generateSharedSecretKey" -> { - if (!call.hasArgument("salt")) result.error("SALT_NULL", null, null); - if (!call.hasArgument("keyBytesCount")) result.error("SIZE_NULL", null, null); - if (!call.hasArgument("ephemeralPrivateKey")) result.error("PRIVATE_KEY_NULL", null, null); - if (!call.hasArgument("otherPublicKey")) result.error("PUBLIC_KEY_NULL", null, null); - if (!call.hasArgument("hkdfAlgorithm")) result.error("ALGORITHM_NULL", null, null); - - val salt : ByteArray = call.argument("salt")!! - val keyBytesCount : Int = call.argument("keyBytesCount")!! - val ephemeralPrivateKey : ByteArray = call.argument("ephemeralPrivateKey")!! - val otherPublicKey : ByteArray = call.argument("otherPublicKey")!! - val hkdfAlgorithm : String = call.argument("hkdfAlgorithm")!! - - result.notImplemented() - } - else -> result.notImplemented() + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, name) + channel.setMethodCallHandler(this) } - } - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } + 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()) + "generateKeyPair" -> result.notImplemented() + "pbkdf2" -> methodCallTask = handlePbkdf2(call.arguments()) + "encrypt" -> methodCallTask = handleEncrypt(call.arguments()) + "decrypt" -> methodCallTask = handleDecrypt(call.arguments()) + "generateSharedSecretKey" -> result.notImplemented() + 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 handleEncrypt(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) + val cipher = cipherAlgorithm.getCipher() + + cipher.encrypt(data, key) + } + } + + private fun handleDecrypt(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) + val cipher = cipherAlgorithm.getCipher() + + cipher.decrypt(data, key) + } + } } 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 new file mode 100644 index 0000000..cbb9706 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt @@ -0,0 +1,59 @@ +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 javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +class AES : Cipher { + override val algorithm: CipherAlgorithm + get() = CipherAlgorithm.aes + +/* override fun encrypt(data: ByteArray, key: ByteArray): ByteArray { + val sk: SecretKey = SecretKeySpec(key, "AES") + val cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + // javax.crypto representation = [CIPHERTEXT(n-16) || TAG(16)] + val bytes = cipher.doFinal(data) + val iv = cipher.iv.copyOf() // 12 bytes nonce + // native.crypto representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] + return iv.plus(bytes) + } + + override fun decrypt(data: ByteArray, key: ByteArray): ByteArray { + val sk: SecretKey = SecretKeySpec(key, "AES") + // native.crypto representation = [NONCE(12) || CIPHERTEXT(n-16) || TAG(16)] + val iv: ByteArray = data.take(12).toByteArray() + // javax.crypto representation = [CIPHERTEXT(n-28) || TAG(16)] + val payload: ByteArray = data.drop(12).toByteArray() + val spec = GCMParameterSpec(16 * 8, iv) + val cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sk, spec) + return cipher.doFinal(payload) + }*/ + + override fun encrypt(data: ByteArray, key: ByteArray): ByteArray { + val sk: SecretKey = SecretKeySpec(key, "AES") + val cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding") + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + // javax.crypto representation = [CIPHERTEXT(n-16)] + val bytes = cipher.doFinal(data) + val iv = cipher.iv + // native.crypto representation = [IV(16) || CIPHERTEXT(n-16)] + return iv.plus(bytes) + } + + override fun decrypt(data: ByteArray, key: ByteArray): ByteArray { + val sk: SecretKey = SecretKeySpec(key, "AES") + // native.crypto representation = [IV(16) || CIPHERTEXT(n-16)] + val iv: ByteArray = data.take(16).toByteArray() + // javax.crypto representation = [CIPHERTEXT(n-16)] + val payload: ByteArray = data.drop(16).toByteArray() + val ivSpec = IvParameterSpec(iv) + val cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding") + cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sk, ivSpec) + return cipher.doFinal(payload) + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AESCipher.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AESCipher.kt deleted file mode 100644 index 941cfde..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AESCipher.kt +++ /dev/null @@ -1,31 +0,0 @@ -package fr.pointcheval.native_crypto_android.ciphers - -import fr.pointcheval.native_crypto_android.Cipher -import javax.crypto.SecretKey -import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.SecretKeySpec - -class AESCipher : Cipher() { - override fun encrypt(data: ByteArray, key: ByteArray): ByteArray { - val sk: SecretKey = SecretKeySpec(key, "AES") - val cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") - cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) - // javax.crypto representation = [CIPHERTEXT(n-28) || TAG(16)] - val bytes = cipher.doFinal(data) - val iv = cipher.iv.copyOf() // 12 bytes nonce - // native.crypto representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] - return iv.plus(bytes) - } - - override fun decrypt(data: ByteArray, key: ByteArray): ByteArray? { - val sk: SecretKey = SecretKeySpec(key, "AES") - // native.crypto representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] - val iv = data.sliceArray(IntRange(0,11)) // 12 bytes nonce - // javax.crypto representation = [CIPHERTEXT(n-28) || TAG(16)] - val payload = data.sliceArray(IntRange(12, data.size - 1)) - val spec = GCMParameterSpec(16 * 8, iv) - val cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") - cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sk, spec) - return cipher.doFinal(payload) - } -} \ 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 new file mode 100644 index 0000000..1667507 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt @@ -0,0 +1,10 @@ +package fr.pointcheval.native_crypto_android.interfaces + +import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm + +interface Cipher { + val algorithm: CipherAlgorithm + + fun encrypt(data: ByteArray, key: ByteArray): ByteArray + fun decrypt(data: ByteArray, key: ByteArray): ByteArray +} \ 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 new file mode 100644 index 0000000..4df212b --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt @@ -0,0 +1,5 @@ +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 new file mode 100644 index 0000000..6bd9216 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt @@ -0,0 +1,10 @@ +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 +} \ 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 new file mode 100644 index 0000000..17d8c5e --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt @@ -0,0 +1,39 @@ +package fr.pointcheval.native_crypto_android.kdf + +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 javax.crypto.SecretKeyFactory +import javax.crypto.spec.PBEKeySpec + +class Pbkdf2( + private val keyBytesCount: Int, private val iterations: Int, + private val hash: HashAlgorithm = HashAlgorithm.sha256 +) : KeyDerivation { + + private var password: String? = null + private var salt: String? = null + + fun init(password: String, salt: String) { + this.password = password + this.salt = salt + } + + override val algorithm: KdfAlgorithm + get() = KdfAlgorithm.pbkdf2 + + override fun derive(): SecretKey { + if (password == null || salt == null) { + throw Exception("Password and Salt must be initialized.") + } + val spec = PBEKeySpec( + password!!.toCharArray(), + salt!!.toByteArray(), + iterations, + keyBytesCount * 8 + ) + val skf: SecretKeyFactory = SecretKeyFactory.getInstance(hash.pbkdf2String()) + return SecretKey(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 new file mode 100644 index 0000000..07e9833 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000..a95185f --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000..1cb359a --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt @@ -0,0 +1,14 @@ +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/HashAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt new file mode 100644 index 0000000..904f7ce --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt @@ -0,0 +1,50 @@ +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/KdfAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt new file mode 100644 index 0000000..4ef9f69 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt @@ -0,0 +1,5 @@ +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 new file mode 100644 index 0000000..d4832de --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt @@ -0,0 +1,44 @@ +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