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'
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
@ -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,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<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 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<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