refactor(android): clean and modernize kotlin code
This commit is contained in:
parent
2fe4172131
commit
41354e3dc4
@ -2,14 +2,14 @@ group 'fr.pointcheval.native_crypto_android'
|
|||||||
version '1.0-SNAPSHOT'
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.50'
|
ext.kotlin_version = '1.6.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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?;
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<String, String> = 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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,18 @@
|
|||||||
package fr.pointcheval.native_crypto_android
|
package fr.pointcheval.native_crypto_android
|
||||||
|
|
||||||
import androidx.annotation.NonNull
|
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.embedding.engine.plugins.FlutterPlugin
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
import io.flutter.plugin.common.MethodChannel.Result
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/** NativeCryptoAndroidPlugin */
|
/** NativeCryptoAndroidPlugin */
|
||||||
class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
||||||
@ -16,94 +21,107 @@ class NativeCryptoAndroidPlugin: FlutterPlugin, MethodCallHandler {
|
|||||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||||
/// when the Flutter Engine is detached from the Activity
|
/// when the Flutter Engine is detached from the Activity
|
||||||
private lateinit var channel: MethodChannel
|
private lateinit var channel: MethodChannel
|
||||||
|
private val name = "plugins.hugop.cl/native_crypto"
|
||||||
|
|
||||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel = MethodChannel(flutterPluginBinding.flutterEngine.dartExecutor, "plugins.hugop.cl/native_crypto")
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, name)
|
||||||
channel.setMethodCallHandler(this)
|
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<ByteArray>("data")!!
|
|
||||||
val algorithm : String = call.argument<String>("algorithm")!!
|
|
||||||
|
|
||||||
result.success(Hash.digest(data, algorithm))
|
|
||||||
}
|
|
||||||
"generateSecretKey" -> {
|
|
||||||
if (!call.hasArgument("bitsCount")) result.error("SIZE_NULL", null, null);
|
|
||||||
|
|
||||||
val bitsCount : Int = call.argument<Int>("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<String>("password")!!
|
|
||||||
val salt : String = call.argument<String>("salt")!!
|
|
||||||
val keyBytesCount : Int = call.argument<Int>("keyBytesCount")!!
|
|
||||||
val iterations : Int = call.argument<Int>("iterations")!!
|
|
||||||
val algorithm : String = call.argument<String>("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<ByteArray>("data")!!
|
|
||||||
val key : ByteArray = call.argument<ByteArray>("key")!!
|
|
||||||
val algorithm : String = call.argument<String>("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<ByteArray>("data")!!
|
|
||||||
val key : ByteArray = call.argument<ByteArray>("key")!!
|
|
||||||
val algorithm : String = call.argument<String>("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<ByteArray>("salt")!!
|
|
||||||
val keyBytesCount : Int = call.argument<Int>("keyBytesCount")!!
|
|
||||||
val ephemeralPrivateKey : ByteArray = call.argument<ByteArray>("ephemeralPrivateKey")!!
|
|
||||||
val otherPublicKey : ByteArray = call.argument<ByteArray>("otherPublicKey")!!
|
|
||||||
val hkdfAlgorithm : String = call.argument<String>("hkdfAlgorithm")!!
|
|
||||||
|
|
||||||
result.notImplemented()
|
|
||||||
}
|
|
||||||
else -> result.notImplemented()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel.setMethodCallHandler(null)
|
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<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 handleEncrypt(arguments: Map<String, Any>?): 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)
|
||||||
|
val cipher = cipherAlgorithm.getCipher()
|
||||||
|
|
||||||
|
cipher.encrypt(data, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDecrypt(arguments: Map<String, Any>?): 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)
|
||||||
|
val cipher = cipherAlgorithm.getCipher()
|
||||||
|
|
||||||
|
cipher.decrypt(data, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package fr.pointcheval.native_crypto_android.interfaces
|
||||||
|
|
||||||
|
interface Key {
|
||||||
|
val bytes: ByteArray
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package fr.pointcheval.native_crypto_android.utils
|
||||||
|
|
||||||
|
enum class KdfAlgorithm {
|
||||||
|
pbkdf2
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
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