Merge remote-tracking branch 'origin/dagger3' into rs
This commit is contained in:
commit
502eb59089
16 changed files with 411 additions and 37 deletions
|
@ -52,6 +52,7 @@ import info.nightscout.androidaps.plugins.source.PoctechPlugin
|
||||||
import info.nightscout.androidaps.plugins.source.TomatoPlugin
|
import info.nightscout.androidaps.plugins.source.TomatoPlugin
|
||||||
import info.nightscout.androidaps.utils.OKDialog.show
|
import info.nightscout.androidaps.utils.OKDialog.show
|
||||||
import info.nightscout.androidaps.utils.SafeParse
|
import info.nightscout.androidaps.utils.SafeParse
|
||||||
|
import info.nightscout.androidaps.utils.protection.PasswordCheck
|
||||||
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
@ -98,6 +99,8 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
||||||
@Inject lateinit var wearPlugin: WearPlugin
|
@Inject lateinit var wearPlugin: WearPlugin
|
||||||
@Inject lateinit var maintenancePlugin: MaintenancePlugin
|
@Inject lateinit var maintenancePlugin: MaintenancePlugin
|
||||||
|
|
||||||
|
@Inject lateinit var passwordCheck: PasswordCheck
|
||||||
|
|
||||||
@Inject lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
@Inject lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
||||||
|
|
||||||
override fun androidInjector(): AndroidInjector<Any> = androidInjector
|
override fun androidInjector(): AndroidInjector<Any> = androidInjector
|
||||||
|
@ -185,7 +188,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
||||||
addPreferencesFromResource(R.xml.pref_datachoices, rootKey)
|
addPreferencesFromResource(R.xml.pref_datachoices, rootKey)
|
||||||
addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey)
|
addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey)
|
||||||
}
|
}
|
||||||
initSummary(preferenceScreen)
|
initSummary(preferenceScreen, pluginId != -1)
|
||||||
for (plugin in pluginStore.plugins) {
|
for (plugin in pluginStore.plugins) {
|
||||||
plugin.preprocessPreferences(this)
|
plugin.preprocessPreferences(this)
|
||||||
}
|
}
|
||||||
|
@ -254,19 +257,19 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
||||||
// Preferences
|
// Preferences
|
||||||
if (pref.getKey() == resourceHelper.gs(R.string.key_settings_protection)) {
|
if (pref.getKey() == resourceHelper.gs(R.string.key_settings_protection)) {
|
||||||
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_settings_password))
|
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_settings_password))
|
||||||
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.PASSWORD.ordinal.toString()
|
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
|
||||||
}
|
}
|
||||||
// Application
|
// Application
|
||||||
// Application
|
// Application
|
||||||
if (pref.getKey() == resourceHelper.gs(R.string.key_application_protection)) {
|
if (pref.getKey() == resourceHelper.gs(R.string.key_application_protection)) {
|
||||||
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_application_password))
|
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_application_password))
|
||||||
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.PASSWORD.ordinal.toString()
|
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
|
||||||
}
|
}
|
||||||
// Bolus
|
// Bolus
|
||||||
// Bolus
|
// Bolus
|
||||||
if (pref.getKey() == resourceHelper.gs(R.string.key_bolus_protection)) {
|
if (pref.getKey() == resourceHelper.gs(R.string.key_bolus_protection)) {
|
||||||
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_bolus_password))
|
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_bolus_password))
|
||||||
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.PASSWORD.ordinal.toString()
|
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pref is EditTextPreference) {
|
if (pref is EditTextPreference) {
|
||||||
|
@ -281,17 +284,59 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pref is Preference) {
|
||||||
|
if ((pref.key != null) && (pref.key.contains("_password"))) {
|
||||||
|
if (sp.getString(pref.key, "").startsWith("hmac:")) {
|
||||||
|
pref.summary = "******"
|
||||||
|
} else {
|
||||||
|
pref.summary = resourceHelper.gs(R.string.password_not_set)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pref?.let { adjustUnitDependentPrefs(it) }
|
pref?.let { adjustUnitDependentPrefs(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initSummary(p: Preference) {
|
private fun initSummary(p: Preference, isSinglePreference: Boolean) {
|
||||||
p.isIconSpaceReserved = false // remove extra spacing on left after migration to androidx
|
p.isIconSpaceReserved = false // remove extra spacing on left after migration to androidx
|
||||||
|
// expand single plugin preference by default
|
||||||
|
if (p is PreferenceScreen && isSinglePreference) {
|
||||||
|
if (p.size > 0 && p.getPreference(0) is PreferenceCategory)
|
||||||
|
(p.getPreference(0) as PreferenceCategory).initialExpandedChildrenCount = Int.MAX_VALUE
|
||||||
|
}
|
||||||
if (p is PreferenceGroup) {
|
if (p is PreferenceGroup) {
|
||||||
for (i in 0 until p.preferenceCount) {
|
for (i in 0 until p.preferenceCount) {
|
||||||
initSummary(p.getPreference(i))
|
initSummary(p.getPreference(i), isSinglePreference)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatePrefSummary(p)
|
updatePrefSummary(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We use Preference and custom editor instead of EditTextPreference
|
||||||
|
// to hash password while it is saved and never have to show it, even hashed
|
||||||
|
|
||||||
|
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||||
|
context?.let { context ->
|
||||||
|
if (preference != null) {
|
||||||
|
if (preference.key == resourceHelper.gs(R.string.key_master_password)) {
|
||||||
|
passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (preference.key == resourceHelper.gs(R.string.key_settings_password)) {
|
||||||
|
passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (preference.key == resourceHelper.gs(R.string.key_bolus_password)) {
|
||||||
|
passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (preference.key == resourceHelper.gs(R.string.key_application_password)) {
|
||||||
|
passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onPreferenceTreeClick(preference)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewFragment
|
||||||
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
|
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
|
||||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment
|
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorFragment
|
||||||
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
|
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
|
||||||
|
import info.nightscout.androidaps.plugins.general.wear.WearFragment
|
||||||
import info.nightscout.androidaps.plugins.insulin.InsulinFragment
|
import info.nightscout.androidaps.plugins.insulin.InsulinFragment
|
||||||
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
|
import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
|
||||||
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
|
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
|
||||||
|
@ -72,6 +73,7 @@ abstract class FragmentsModule {
|
||||||
@ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment
|
@ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment
|
||||||
@ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment
|
@ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment
|
||||||
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment
|
@ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment
|
||||||
|
@ContributesAndroidInjector abstract fun contributesWearFragment(): WearFragment
|
||||||
|
|
||||||
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
|
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
|
||||||
@ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment
|
@ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment
|
||||||
|
|
142
app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt
Normal file
142
app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package info.nightscout.androidaps.utils
|
||||||
|
|
||||||
|
import org.spongycastle.util.encoders.Base64
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.security.spec.KeySpec
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.SecretKey
|
||||||
|
import javax.crypto.SecretKeyFactory
|
||||||
|
import javax.crypto.spec.GCMParameterSpec
|
||||||
|
import javax.crypto.spec.PBEKeySpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
private val HEX_CHARS = "0123456789abcdef"
|
||||||
|
private val HEX_CHARS_ARRAY = "0123456789abcdef".toCharArray()
|
||||||
|
|
||||||
|
fun String.hexStringToByteArray() : ByteArray {
|
||||||
|
|
||||||
|
val upperCased = this.toLowerCase()
|
||||||
|
val result = ByteArray(length / 2)
|
||||||
|
for (i in 0 until length step 2) {
|
||||||
|
val firstIndex = HEX_CHARS.indexOf(upperCased[i]);
|
||||||
|
val secondIndex = HEX_CHARS.indexOf(upperCased[i + 1]);
|
||||||
|
|
||||||
|
val octet = firstIndex.shl(4).or(secondIndex)
|
||||||
|
result.set(i.shr(1), octet.toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.toHex() : String{
|
||||||
|
val result = StringBuffer()
|
||||||
|
|
||||||
|
forEach {
|
||||||
|
val octet = it.toInt()
|
||||||
|
val firstIndex = (octet and 0xF0).ushr(4)
|
||||||
|
val secondIndex = octet and 0x0F
|
||||||
|
result.append(HEX_CHARS_ARRAY[firstIndex])
|
||||||
|
result.append(HEX_CHARS_ARRAY[secondIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
object CryptoUtil {
|
||||||
|
|
||||||
|
private const val IV_LENGTH_BYTE = 12
|
||||||
|
private const val TAG_LENGTH_BIT = 128
|
||||||
|
private const val AES_KEY_SIZE_BIT = 256
|
||||||
|
private const val PBKDF2_ITERATIONS = 50000 // check delays it cause on real device
|
||||||
|
private const val SALT_SIZE_BYTE = 32
|
||||||
|
|
||||||
|
private val secureRandom: SecureRandom = SecureRandom()
|
||||||
|
|
||||||
|
fun sha256(source: String): String {
|
||||||
|
val digest = MessageDigest.getInstance("SHA-256")
|
||||||
|
val hashRaw = digest.digest(source.toByteArray())
|
||||||
|
return hashRaw.toHex()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hmac256(str: String, secret: String): String? {
|
||||||
|
val sha256_HMAC = Mac.getInstance("HmacSHA256")
|
||||||
|
val secretKey = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
|
||||||
|
sha256_HMAC.init(secretKey)
|
||||||
|
return sha256_HMAC.doFinal(str.toByteArray()).toHex()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepCipherKey(passPhrase: String, salt:ByteArray, iterationCount:Int = PBKDF2_ITERATIONS, keyStrength:Int = AES_KEY_SIZE_BIT): SecretKeySpec {
|
||||||
|
val factory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1")
|
||||||
|
val spec: KeySpec = PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount, keyStrength)
|
||||||
|
val tmp: SecretKey = factory.generateSecret(spec)
|
||||||
|
return SecretKeySpec(tmp.getEncoded(), "AES")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mineSalt(len :Int = SALT_SIZE_BYTE): ByteArray {
|
||||||
|
val salt = ByteArray(len)
|
||||||
|
secureRandom.nextBytes(salt)
|
||||||
|
return salt
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encrypt(passPhrase: String, salt:ByteArray, rawData: String ): String? {
|
||||||
|
val iv: ByteArray?
|
||||||
|
val encrypted: ByteArray?
|
||||||
|
return try {
|
||||||
|
iv = ByteArray(IV_LENGTH_BYTE)
|
||||||
|
secureRandom.nextBytes(iv)
|
||||||
|
val cipherEnc: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
|
cipherEnc.init(Cipher.ENCRYPT_MODE, prepCipherKey(passPhrase, salt), GCMParameterSpec(TAG_LENGTH_BIT, iv))
|
||||||
|
encrypted = cipherEnc.doFinal(rawData.toByteArray())
|
||||||
|
val byteBuffer: ByteBuffer = ByteBuffer.allocate(1 + iv.size + encrypted.size)
|
||||||
|
byteBuffer.put(iv.size.toByte())
|
||||||
|
byteBuffer.put(iv)
|
||||||
|
byteBuffer.put(encrypted)
|
||||||
|
String(Base64.encode(byteBuffer.array()))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrypt(passPhrase: String, salt:ByteArray, encryptedData: String): String? {
|
||||||
|
val iv: ByteArray?
|
||||||
|
val encrypted: ByteArray?
|
||||||
|
return try {
|
||||||
|
val byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData))
|
||||||
|
val ivLength = byteBuffer.get().toInt()
|
||||||
|
iv = ByteArray(ivLength)
|
||||||
|
byteBuffer[iv]
|
||||||
|
encrypted = ByteArray(byteBuffer.remaining())
|
||||||
|
byteBuffer[encrypted]
|
||||||
|
val cipherDec: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
|
cipherDec.init(Cipher.DECRYPT_MODE, prepCipherKey(passPhrase, salt), GCMParameterSpec(TAG_LENGTH_BIT, iv))
|
||||||
|
val dec = cipherDec.doFinal(encrypted)
|
||||||
|
String(dec)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkPassword(password: String, referenceHash: String): Boolean {
|
||||||
|
return if (referenceHash.startsWith("hmac:")) {
|
||||||
|
val hashSegments = referenceHash.split(":")
|
||||||
|
if (hashSegments.size != 3)
|
||||||
|
return false
|
||||||
|
return hmac256(password, hashSegments[1]) == hashSegments[2]
|
||||||
|
} else {
|
||||||
|
password == referenceHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hashPassword(password: String): String {
|
||||||
|
return if (!password.startsWith("hmac:")) {
|
||||||
|
val salt = mineSalt().toHex()
|
||||||
|
return "hmac:${salt}:${hmac256(password, salt)}"
|
||||||
|
} else {
|
||||||
|
password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,50 +2,117 @@ package info.nightscout.androidaps.utils.protection
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.utils.CryptoUtil
|
||||||
import info.nightscout.androidaps.utils.ToastUtils
|
import info.nightscout.androidaps.utils.ToastUtils
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
// since androidx.autofill.HintConstants are not available
|
||||||
|
val AUTOFILL_HINT_NEW_PASSWORD = "newPassword"
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class PasswordCheck @Inject constructor(val sp: SP) {
|
class PasswordCheck @Inject constructor(val sp: SP) {
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
fun queryPassword(activity: FragmentActivity, @StringRes labelId: Int, @StringRes preference: Int, ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) {
|
fun queryPassword(activity: FragmentActivity, @StringRes labelId: Int, @StringRes preference: Int, ok: ( (String) -> Unit)?, cancel: (()->Unit)? = null, fail: (()->Unit)? = null) {
|
||||||
val password = sp.getString(preference, "")
|
val password = sp.getString(preference, "")
|
||||||
if (password == "") {
|
if (password == "") {
|
||||||
ok?.run()
|
ok?.invoke("")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null)
|
||||||
|
(titleLayout.findViewById<View>(R.id.alertdialog_title) as TextView).text = activity.getString(labelId)
|
||||||
|
(titleLayout.findViewById<View>(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_key_48dp)
|
||||||
|
|
||||||
val promptsView = LayoutInflater.from(activity).inflate(R.layout.passwordprompt, null)
|
val promptsView = LayoutInflater.from(activity).inflate(R.layout.passwordprompt, null)
|
||||||
|
|
||||||
val alertDialogBuilder = AlertDialog.Builder(activity)
|
val alertDialogBuilder = AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme))
|
||||||
alertDialogBuilder.setView(promptsView)
|
alertDialogBuilder.setView(promptsView)
|
||||||
|
|
||||||
val label = promptsView.findViewById<View>(R.id.passwordprompt_text) as TextView
|
|
||||||
label.text = activity.getString(labelId)
|
|
||||||
val userInput = promptsView.findViewById<View>(R.id.passwordprompt_pass) as EditText
|
val userInput = promptsView.findViewById<View>(R.id.passwordprompt_pass) as EditText
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val autoFillHintPasswordKind = activity.getString(preference)
|
||||||
|
userInput.setAutofillHints(View.AUTOFILL_HINT_PASSWORD, "aaps_${autoFillHintPasswordKind}")
|
||||||
|
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
|
||||||
|
}
|
||||||
|
|
||||||
alertDialogBuilder
|
alertDialogBuilder
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
|
.setCustomTitle(titleLayout)
|
||||||
.setPositiveButton(activity.getString(R.string.ok)) { _, _ ->
|
.setPositiveButton(activity.getString(R.string.ok)) { _, _ ->
|
||||||
val enteredPassword = userInput.text.toString()
|
val enteredPassword = userInput.text.toString()
|
||||||
if (password == enteredPassword) ok?.run()
|
if (CryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword)
|
||||||
else {
|
else {
|
||||||
ToastUtils.showToastInUiThread(activity, activity.getString(R.string.wrongpassword))
|
ToastUtils.showToastInUiThread(activity, activity.getString(R.string.wrongpassword))
|
||||||
fail?.run()
|
fail?.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(activity.getString(R.string.cancel)
|
.setNegativeButton(activity.getString(R.string.cancel)
|
||||||
) { dialog, _ ->
|
) { dialog, _ ->
|
||||||
cancel?.run()
|
cancel?.invoke()
|
||||||
|
dialog.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialogBuilder.create().show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InflateParams")
|
||||||
|
fun setPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ( (String) -> Unit)? = null, cancel: (()->Unit)? = null, clear: (()->Unit)? = null) {
|
||||||
|
val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
|
||||||
|
|
||||||
|
val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null)
|
||||||
|
(titleLayout.findViewById<View>(R.id.alertdialog_title) as TextView).text = context.getText(labelId)
|
||||||
|
(titleLayout.findViewById<View>(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_key_48dp)
|
||||||
|
|
||||||
|
val alertDialogBuilder = AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme))
|
||||||
|
alertDialogBuilder.setView(promptsView)
|
||||||
|
|
||||||
|
val userInput = promptsView.findViewById<View>(R.id.passwordprompt_pass) as EditText
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val autoFillHintPasswordKind = context.getString(preference)
|
||||||
|
userInput.setAutofillHints(AUTOFILL_HINT_NEW_PASSWORD, "aaps_${autoFillHintPasswordKind}")
|
||||||
|
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialogBuilder
|
||||||
|
.setCancelable(false)
|
||||||
|
.setCustomTitle(titleLayout)
|
||||||
|
.setPositiveButton(context.getString(R.string.ok)) { _, _ ->
|
||||||
|
val enteredPassword = userInput.text.toString()
|
||||||
|
if (enteredPassword.isNotEmpty()) {
|
||||||
|
sp.putString(preference, CryptoUtil.hashPassword(enteredPassword))
|
||||||
|
ToastUtils.showToastInUiThread(context, context.getString(R.string.password_set))
|
||||||
|
ok?.invoke(enteredPassword)
|
||||||
|
} else {
|
||||||
|
if (sp.contains(preference)) {
|
||||||
|
sp.remove(preference)
|
||||||
|
ToastUtils.showToastInUiThread(context, context.getString(R.string.password_cleared))
|
||||||
|
clear?.invoke()
|
||||||
|
} else {
|
||||||
|
ToastUtils.showToastInUiThread(context, context.getString(R.string.password_not_changed))
|
||||||
|
cancel?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.setNegativeButton(context.getString(R.string.cancel)
|
||||||
|
) { dialog, _ ->
|
||||||
|
ToastUtils.showToastInUiThread(context, context.getString(R.string.password_not_changed))
|
||||||
|
cancel?.invoke()
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ class ProtectionCheck @Inject constructor(
|
||||||
enum class ProtectionType {
|
enum class ProtectionType {
|
||||||
NONE,
|
NONE,
|
||||||
BIOMETRIC,
|
BIOMETRIC,
|
||||||
PASSWORD
|
MASTER_PASSWORD,
|
||||||
|
CUSTOM_PASSWORD
|
||||||
}
|
}
|
||||||
|
|
||||||
private val passwordsResourceIDs = listOf(
|
private val passwordsResourceIDs = listOf(
|
||||||
|
@ -43,7 +44,8 @@ class ProtectionCheck @Inject constructor(
|
||||||
return when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) {
|
return when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) {
|
||||||
ProtectionType.NONE -> false
|
ProtectionType.NONE -> false
|
||||||
ProtectionType.BIOMETRIC -> true
|
ProtectionType.BIOMETRIC -> true
|
||||||
ProtectionType.PASSWORD -> sp.getString(passwordsResourceIDs[protection.ordinal], "") != ""
|
ProtectionType.MASTER_PASSWORD -> sp.getString(R.string.key_master_password, "") != ""
|
||||||
|
ProtectionType.CUSTOM_PASSWORD -> sp.getString(passwordsResourceIDs[protection.ordinal], "") != ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,8 +57,10 @@ class ProtectionCheck @Inject constructor(
|
||||||
ok?.run()
|
ok?.run()
|
||||||
ProtectionType.BIOMETRIC ->
|
ProtectionType.BIOMETRIC ->
|
||||||
BiometricCheck.biometricPrompt(activity, titleResourceIDs[protection.ordinal], ok, cancel, fail)
|
BiometricCheck.biometricPrompt(activity, titleResourceIDs[protection.ordinal], ok, cancel, fail)
|
||||||
ProtectionType.PASSWORD ->
|
ProtectionType.MASTER_PASSWORD ->
|
||||||
passwordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], ok, cancel, fail)
|
passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { ok?.run() }, { cancel?.run() }, { fail?.run() })
|
||||||
|
ProtectionType.CUSTOM_PASSWORD ->
|
||||||
|
passwordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
9
app/src/main/res/drawable/ic_key.xml
Normal file
9
app/src/main/res/drawable/ic_key.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp">
|
||||||
|
<path
|
||||||
|
android:pathData="M22 18V22H18V19H15V16H12L9.74 13.74C9.19 13.91 8.61 14 8 14A6 6 0 0 1 2 8A6 6 0 0 1 8 2A6 6 0 0 1 14 8C14 8.61 13.91 9.19 13.74 9.74L22 18M7 5A2 2 0 0 0 5 7A2 2 0 0 0 7 9A2 2 0 0 0 9 7A2 2 0 0 0 7 5Z"
|
||||||
|
android:fillColor="#ffffff" />
|
||||||
|
</vector>
|
17
app/src/main/res/drawable/ic_key_48dp.xml
Normal file
17
app/src/main/res/drawable/ic_key_48dp.xml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
>
|
||||||
|
<group
|
||||||
|
android:scaleX="0.66"
|
||||||
|
android:scaleY="0.66"
|
||||||
|
android:pivotX="12"
|
||||||
|
android:pivotY="12">
|
||||||
|
<path
|
||||||
|
android:pathData="M22 18V22H18V19H15V16H12L9.74 13.74C9.19 13.91 8.61 14 8 14A6 6 0 0 1 2 8A6 6 0 0 1 8 2A6 6 0 0 1 14 8C14 8.61 13.91 9.19 13.74 9.74L22 18M7 5A2 2 0 0 0 5 7A2 2 0 0 0 7 9A2 2 0 0 0 9 7A2 2 0 0 0 7 5Z"
|
||||||
|
android:fillColor="#FF000000" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -5,17 +5,13 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="10dp" >
|
android:padding="10dp" >
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/passwordprompt_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/passwordprompt_pass"
|
android:id="@+id/passwordprompt_pass"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="textPassword">
|
android:hint="@string/password_hint"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
>
|
||||||
|
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
|
|
||||||
|
|
|
@ -280,7 +280,7 @@
|
||||||
<string name="danarprofile">DanaR profil</string>
|
<string name="danarprofile">DanaR profil</string>
|
||||||
<string name="danarprofile_dia">DIA [h]</string>
|
<string name="danarprofile_dia">DIA [h]</string>
|
||||||
<string name="danarprofile_dia_summary">Celková doba aktivity inzulínu</string>
|
<string name="danarprofile_dia_summary">Celková doba aktivity inzulínu</string>
|
||||||
<string name="failedupdatebasalprofile">Chyba při nastavení dočasného bazálu</string>
|
<string name="failedupdatebasalprofile">Chyba při nastavení bazálního pprofilu</string>
|
||||||
<string name="danar_historyreload">Načíst</string>
|
<string name="danar_historyreload">Načíst</string>
|
||||||
<string name="uploading">Nahrávám</string>
|
<string name="uploading">Nahrávám</string>
|
||||||
<string name="danar_ebolus">E bolus</string>
|
<string name="danar_ebolus">E bolus</string>
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<string name="objectives_useloop">Affichage du contenu du plugin Boucle</string>
|
<string name="objectives_useloop">Affichage du contenu du plugin Boucle</string>
|
||||||
<string name="objectives_usescale">Modification de l\'échelle du graphique par un appui long sur la courbe de glycémie</string>
|
<string name="objectives_usescale">Modification de l\'échelle du graphique par un appui long sur la courbe de glycémie</string>
|
||||||
<string name="objectives_button_enter">Entrer</string>
|
<string name="objectives_button_enter">Entrer</string>
|
||||||
|
<string name="enter_code_obtained_from_developers_to_bypass_the_rest_of_objectives">Si vous avez au moins 3 mois d\'expérience de boucle fermée avec d\'autres systèmes, vous pourriez avoir droit à un code permettant d\'ignorer les objectifs. Voir https://androidaps.readthedocs.io/en/latest/CROWDIN/fr/Usage/Objectives.html#ignorer-les-objectifs pour plus de détails.</string>
|
||||||
<string name="codeaccepted">Code accepté</string>
|
<string name="codeaccepted">Code accepté</string>
|
||||||
<string name="codeinvalid">Code invalide</string>
|
<string name="codeinvalid">Code invalide</string>
|
||||||
<string name="objectives_exam_objective">Prouver ses connaissances</string>
|
<string name="objectives_exam_objective">Prouver ses connaissances</string>
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<string name="objectives_useloop">Visa innehållet i insticksprogrammet \"Loop\"</string>
|
<string name="objectives_useloop">Visa innehållet i insticksprogrammet \"Loop\"</string>
|
||||||
<string name="objectives_usescale">Testa skala om BG-grafen genom att trycka och hålla in fingret på den</string>
|
<string name="objectives_usescale">Testa skala om BG-grafen genom att trycka och hålla in fingret på den</string>
|
||||||
<string name="objectives_button_enter">Enter</string>
|
<string name="objectives_button_enter">Enter</string>
|
||||||
|
<string name="enter_code_obtained_from_developers_to_bypass_the_rest_of_objectives">Om du har minst 3 månaders erfarenhet av closed loop med andra system kan du kvalificera dig för en kod för att hoppa över mål. Se https://androidaps.readthedocs.io/en/latest/EN/Usage/Objectives.html#skip-objectives för mer info.</string>
|
||||||
<string name="codeaccepted">Koden godkänd</string>
|
<string name="codeaccepted">Koden godkänd</string>
|
||||||
<string name="codeinvalid">Koden är felaktig</string>
|
<string name="codeinvalid">Koden är felaktig</string>
|
||||||
<string name="objectives_exam_objective">Bevisa dina kunskaper</string>
|
<string name="objectives_exam_objective">Bevisa dina kunskaper</string>
|
||||||
|
|
|
@ -1454,4 +1454,5 @@ Eversense-appen.</string>
|
||||||
<string name="loop_smbexecution_time_label">SMB utförd</string>
|
<string name="loop_smbexecution_time_label">SMB utförd</string>
|
||||||
<string name="loop_tbrrequest_time_label">Basalförändring begärd</string>
|
<string name="loop_tbrrequest_time_label">Basalförändring begärd</string>
|
||||||
<string name="loop_tbrexecution_time_label">Basalförändring utförd</string>
|
<string name="loop_tbrexecution_time_label">Basalförändring utförd</string>
|
||||||
|
<string name="insight_alert_notification_channel">Pumpvarningar Insight</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -5,15 +5,23 @@
|
||||||
<string name="settings_protection">Settings protection</string>
|
<string name="settings_protection">Settings protection</string>
|
||||||
<string name="application_protection">Application protection</string>
|
<string name="application_protection">Application protection</string>
|
||||||
<string name="bolus_protection">Bolus protection</string>
|
<string name="bolus_protection">Bolus protection</string>
|
||||||
|
<string name="master_password">Master password</string>
|
||||||
<string name="settings_password">Settings password</string>
|
<string name="settings_password">Settings password</string>
|
||||||
<string name="application_password">Application password</string>
|
<string name="application_password">Application password</string>
|
||||||
<string name="bolus_password">Bolus password</string>
|
<string name="bolus_password">Bolus password</string>
|
||||||
<string name="unlock_settings">Unlock settings</string>
|
<string name="unlock_settings">Unlock settings</string>
|
||||||
<string name="biometric">Biometric</string>
|
<string name="biometric">Biometric</string>
|
||||||
<string name="password">Password</string>
|
<string name="custom_password">Custom password</string>
|
||||||
<string name="noprotection">No protection</string>
|
<string name="noprotection">No protection</string>
|
||||||
<string name="protection">Protection</string>
|
<string name="protection">Protection</string>
|
||||||
|
|
||||||
|
<string name="password_set">Password set!</string>
|
||||||
|
<string name="password_not_set">Password not set</string>
|
||||||
|
<string name="password_not_changed">Password not changed</string>
|
||||||
|
<string name="password_cleared">Password cleared!</string>
|
||||||
|
<string name="password_hint">Enter password here</string>
|
||||||
|
|
||||||
|
<string name="key_master_password">master_password</string>
|
||||||
<string name="key_settings_password" translatable="false">settings_password</string>
|
<string name="key_settings_password" translatable="false">settings_password</string>
|
||||||
<string name="key_application_password" translatable="false">application_password</string>
|
<string name="key_application_password" translatable="false">application_password</string>
|
||||||
<string name="key_bolus_password">translatable="false"bolus_password</string>
|
<string name="key_bolus_password">translatable="false"bolus_password</string>
|
||||||
|
@ -24,13 +32,15 @@
|
||||||
<string-array name="protectiontype">
|
<string-array name="protectiontype">
|
||||||
<item>@string/noprotection</item>
|
<item>@string/noprotection</item>
|
||||||
<item>@string/biometric</item>
|
<item>@string/biometric</item>
|
||||||
<item>@string/password</item>
|
<item>@string/master_password</item>
|
||||||
|
<item>@string/custom_password</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="protectiontypeValues">
|
<string-array name="protectiontypeValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
<item>2</item>
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -22,6 +22,12 @@
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/protection">
|
<PreferenceCategory android:title="@string/protection">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:key="@string/key_master_password"
|
||||||
|
android:title="@string/master_password"
|
||||||
|
/>
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="1"
|
android:defaultValue="1"
|
||||||
android:entries="@array/protectiontype"
|
android:entries="@array/protectiontype"
|
||||||
|
@ -29,7 +35,7 @@
|
||||||
android:key="@string/key_settings_protection"
|
android:key="@string/key_settings_protection"
|
||||||
android:title="@string/settings_protection" />
|
android:title="@string/settings_protection" />
|
||||||
|
|
||||||
<EditTextPreference
|
<Preference
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:key="@string/key_settings_password"
|
android:key="@string/key_settings_password"
|
||||||
android:title="@string/settings_password" />
|
android:title="@string/settings_password" />
|
||||||
|
@ -41,7 +47,7 @@
|
||||||
android:key="@string/key_application_protection"
|
android:key="@string/key_application_protection"
|
||||||
android:title="@string/application_protection" />
|
android:title="@string/application_protection" />
|
||||||
|
|
||||||
<EditTextPreference
|
<Preference
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:key="@string/key_application_password"
|
android:key="@string/key_application_password"
|
||||||
android:title="@string/application_password" />
|
android:title="@string/application_password" />
|
||||||
|
@ -53,7 +59,7 @@
|
||||||
android:key="@string/key_bolus_protection"
|
android:key="@string/key_bolus_protection"
|
||||||
android:title="@string/bolus_protection" />
|
android:title="@string/bolus_protection" />
|
||||||
|
|
||||||
<EditTextPreference
|
<Preference
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:key="@string/key_bolus_password"
|
android:key="@string/key_bolus_password"
|
||||||
android:title="@string/bolus_password" />
|
android:title="@string/bolus_password" />
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:dependency="wearcontrol"
|
android:dependency="wearcontrol"
|
||||||
android:summary="@string/wear_wizard_settings_summary"
|
android:summary="@string/wear_wizard_settings_summary"
|
||||||
android:title="@string/wear_wizard_settings"
|
android:title="@string/wear_wizard_settings">
|
||||||
app:initialExpandedChildrenCount="0">
|
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
|
@ -57,8 +56,7 @@
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/wear_display_settings"
|
android:title="@string/wear_display_settings">
|
||||||
app:initialExpandedChildrenCount="0">
|
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
@ -86,8 +84,7 @@
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/wear_general_settings"
|
android:title="@string/wear_general_settings">
|
||||||
app:initialExpandedChildrenCount="0">
|
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package info.nightscout.androidaps.utils
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.TestBase
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.powermock.core.classloader.annotations.PowerMockIgnore
|
||||||
|
import org.powermock.modules.junit4.PowerMockRunner
|
||||||
|
|
||||||
|
@PowerMockIgnore("javax.crypto.*")
|
||||||
|
@RunWith(PowerMockRunner::class)
|
||||||
|
class CryptoUtilTest: TestBase() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFixedSaltCrypto() {
|
||||||
|
val salt = byteArrayOf(
|
||||||
|
-33, -29, 16, -19, 99, -111, -3, 2, 116, 106, 47, 38, -54, 11, -77, 28,
|
||||||
|
111, -15, -65, -110, 4, -32, -29, -70, -95, -88, -53, 19, 87, -103, 123, -15)
|
||||||
|
|
||||||
|
val password = "thisIsFixedPassword"
|
||||||
|
val payload = "FIXED-PAYLOAD"
|
||||||
|
val encrypted = CryptoUtil.encrypt(password, salt, payload)
|
||||||
|
|
||||||
|
Assert.assertNotNull(encrypted)
|
||||||
|
val decrypted = CryptoUtil.decrypt(password, salt, encrypted!!)
|
||||||
|
Assert.assertEquals(decrypted, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testStandardCrypto() {
|
||||||
|
val salt = CryptoUtil.mineSalt()
|
||||||
|
|
||||||
|
val password = "topSikret"
|
||||||
|
val payload = "{what:payloadYouWantToProtect}"
|
||||||
|
val encrypted = CryptoUtil.encrypt(password, salt, payload)
|
||||||
|
|
||||||
|
Assert.assertNotNull(encrypted)
|
||||||
|
val decrypted = CryptoUtil.decrypt(password, salt, encrypted!!)
|
||||||
|
Assert.assertEquals(decrypted, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testHashVector() {
|
||||||
|
val payload = "{what:payloadYouWantToProtect}"
|
||||||
|
val hash = CryptoUtil.sha256(payload)
|
||||||
|
Assert.assertEquals(hash, "a1aafe3ed6cc127e6d102ddbc40a205147230e9cfd178daf108c83543bbdcd13")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testHmac() {
|
||||||
|
val payload = "{what:payloadYouWantToProtect}"
|
||||||
|
val password = "topSikret"
|
||||||
|
val expectedHmac = "ea2213953d0f2e55047cae2d23fb4f0de1b805d55e6271efa70d6b85fb692bea" // generated using other HMAC tool
|
||||||
|
val hash = CryptoUtil.hmac256(payload, password)
|
||||||
|
Assert.assertEquals(hash, expectedHmac)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPlainPasswordCheck() {
|
||||||
|
Assert.assertTrue(CryptoUtil.checkPassword("same", "same"))
|
||||||
|
Assert.assertFalse(CryptoUtil.checkPassword("same", "other"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testHashedPasswordCheck() {
|
||||||
|
Assert.assertTrue(CryptoUtil.checkPassword("givenSecret", CryptoUtil.hashPassword("givenSecret")))
|
||||||
|
Assert.assertFalse(CryptoUtil.checkPassword("givenSecret", CryptoUtil.hashPassword("otherSecret")))
|
||||||
|
|
||||||
|
Assert.assertTrue(CryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1"))
|
||||||
|
Assert.assertFalse(CryptoUtil.checkPassword("givenMashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1"))
|
||||||
|
Assert.assertFalse(CryptoUtil.checkPassword("givenHashToCheck", "hmac:0fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1"))
|
||||||
|
Assert.assertFalse(CryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:b0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue