Changed the method used to store the user selected PIN/Pattern into the shared preferences, the app now uses a hashed version of the PIN/Pattern with added salt at the beginning instead of an encrypted one. The salt is being saved into the shared preferences so that we can confirm the current PIN/Pattern when the user requires to do so.
This commit is contained in:
parent
b0811ab2c8
commit
fac7bb031e
10 changed files with 104 additions and 55 deletions
|
@ -37,7 +37,7 @@ abstract class BaseAccountFragment : ConnectedFragment() {
|
||||||
// Stores the user selected PIN encrypted
|
// Stores the user selected PIN encrypted
|
||||||
PreferenceManager.getDefaultSharedPreferences(context!!)
|
PreferenceManager.getDefaultSharedPreferences(context!!)
|
||||||
.edit()
|
.edit()
|
||||||
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPIN)
|
.putString(Constants.KEY_HASHED_PIN_PATTERN, encryptedPIN)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
// Stores the accounts this key refers to
|
// Stores the accounts this key refers to
|
||||||
|
|
|
@ -54,16 +54,22 @@ abstract class BaseSecurityLockDialog : DialogFragment() {
|
||||||
/** Keeps track of all RxJava disposables, to make sure they are all disposed when the fragment is destroyed */
|
/** Keeps track of all RxJava disposables, to make sure they are all disposed when the fragment is destroyed */
|
||||||
protected var mDisposables = CompositeDisposable()
|
protected var mDisposables = CompositeDisposable()
|
||||||
|
|
||||||
/** Current encrypted version of the PIN/Pattern */
|
/** Current hashed version of the salt + PIN/Pattern */
|
||||||
protected var currentEncryptedPINPattern: String? = null
|
protected var currentHashedPINPattern: String? = null
|
||||||
|
|
||||||
|
/** Salt used to hash the current PIN/Pattern */
|
||||||
|
protected var currentPINPatternSalt: String? = null
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
onAttachToParentFragment(parentFragment)
|
onAttachToParentFragment(parentFragment)
|
||||||
|
|
||||||
currentEncryptedPINPattern = PreferenceManager.getDefaultSharedPreferences(context)
|
currentHashedPINPattern = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getString(Constants.KEY_ENCRYPTED_PIN, "")?.trim()
|
.getString(Constants.KEY_HASHED_PIN_PATTERN, "")
|
||||||
|
|
||||||
|
currentPINPatternSalt = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getString(Constants.KEY_PIN_PATTERN_SALT, "")
|
||||||
|
|
||||||
currentStep = arguments?.getInt(KEY_STEP_SECURITY_LOCK) ?: -1
|
currentStep = arguments?.getInt(KEY_STEP_SECURITY_LOCK) ?: -1
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||||
|
import cy.agorise.bitsybitshareswallet.utils.hideKeyboard
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.dialog_pin_security_lock.*
|
import kotlinx.android.synthetic.main.dialog_pin_security_lock.*
|
||||||
|
|
||||||
|
@ -39,16 +40,20 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
var handled = false
|
var handled = false
|
||||||
if (actionId == EditorInfo.IME_ACTION_GO) {
|
if (actionId == EditorInfo.IME_ACTION_GO) {
|
||||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||||
// The user just wants to verify the current encrypted PIN/Pattern
|
// The user just wants to verify the current hashed PIN/Pattern
|
||||||
val encryptedPIN = CryptoUtils.encrypt(v.context, v.text.toString()).trim()
|
val pin = v.text.toString().trim()
|
||||||
|
val hashedPIN = CryptoUtils.createSHA256Hash(currentPINPatternSalt + pin)
|
||||||
|
|
||||||
if (encryptedPIN == currentEncryptedPINPattern) {
|
if (hashedPIN == currentHashedPINPattern) {
|
||||||
// PIN is correct, proceed
|
// PIN is correct, proceed
|
||||||
|
tietPIN.hideKeyboard()
|
||||||
|
rootView.requestFocus()
|
||||||
dismiss()
|
dismiss()
|
||||||
mCallback?.onPINPatternEntered(actionIdentifier)
|
mCallback?.onPINPatternEntered(actionIdentifier)
|
||||||
} else {
|
} else {
|
||||||
tilPIN.error = getString(R.string.error__wrong_pin)
|
tilPIN.error = getString(R.string.error__wrong_pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
||||||
// The user is trying to create a new PIN
|
// The user is trying to create a new PIN
|
||||||
if (v.text.toString().trim().length >= Constants.MIN_PIN_LENGTH) {
|
if (v.text.toString().trim().length >= Constants.MIN_PIN_LENGTH) {
|
||||||
|
@ -62,12 +67,14 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
if (pinConfirm != newPIN) {
|
if (pinConfirm != newPIN) {
|
||||||
tvTitle.text = getString(R.string.title__pins_dont_match)
|
tvTitle.text = getString(R.string.title__pins_dont_match)
|
||||||
} else {
|
} else {
|
||||||
val encryptedPIN = CryptoUtils.encrypt(v.context, v.text.toString()).trim()
|
val salt = CryptoUtils.generateSalt()
|
||||||
|
val hashedPIN = CryptoUtils.createSHA256Hash(salt + pinConfirm)
|
||||||
|
|
||||||
// Stores the newly selected PIN, encrypted
|
// Stores the newly selected PIN, hashed
|
||||||
PreferenceManager.getDefaultSharedPreferences(v.context).edit()
|
PreferenceManager.getDefaultSharedPreferences(v.context).edit()
|
||||||
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPIN)
|
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPIN)
|
||||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0).apply() // 0 -> PIN
|
.putString(Constants.KEY_PIN_PATTERN_SALT, salt)
|
||||||
|
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 1).apply() // 1 -> PIN
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
mCallback?.onPINPatternChanged()
|
mCallback?.onPINPatternChanged()
|
||||||
|
|
|
@ -64,8 +64,9 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
override fun onComplete(pattern: List<PatternLockView.Dot>) {
|
override fun onComplete(pattern: List<PatternLockView.Dot>) {
|
||||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||||
context?.let {
|
context?.let {
|
||||||
val encryptedPattern = CryptoUtils.encrypt(it, getStringPattern(pattern)).trim()
|
val hashedPattern = CryptoUtils.createSHA256Hash(currentPINPatternSalt +
|
||||||
if (encryptedPattern == currentEncryptedPINPattern) {
|
getStringPattern(pattern))
|
||||||
|
if (hashedPattern == currentHashedPINPattern) {
|
||||||
// Pattern is correct, proceed
|
// Pattern is correct, proceed
|
||||||
dismiss()
|
dismiss()
|
||||||
mCallback?.onPINPatternEntered(actionIdentifier)
|
mCallback?.onPINPatternEntered(actionIdentifier)
|
||||||
|
@ -102,12 +103,14 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
btnNext.isEnabled = true
|
btnNext.isEnabled = true
|
||||||
btnNext.setOnClickListener {
|
btnNext.setOnClickListener {
|
||||||
context?.let {
|
context?.let {
|
||||||
val encryptedPattern = CryptoUtils.encrypt(it, patternConfirm).trim()
|
val salt = CryptoUtils.generateSalt()
|
||||||
|
val hashedPattern = CryptoUtils.createSHA256Hash(salt + patternConfirm)
|
||||||
|
|
||||||
// Stores the newly selected Pattern, encrypted
|
// Stores the newly selected Pattern, encrypted
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).edit()
|
PreferenceManager.getDefaultSharedPreferences(it).edit()
|
||||||
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPattern)
|
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPattern)
|
||||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 1).apply() // 1 -> Pattern
|
.putString(Constants.KEY_PIN_PATTERN_SALT, salt)
|
||||||
|
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 2).apply() // 2 -> Pattern
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
mCallback?.onPINPatternChanged()
|
mCallback?.onPINPatternChanged()
|
||||||
|
|
|
@ -465,9 +465,9 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||||
// Security Lock Options
|
// Security Lock Options
|
||||||
// 0 -> PIN
|
// 0 -> None
|
||||||
// 1 -> Pattern
|
// 1 -> PIN
|
||||||
// 2 -> None
|
// 2 -> Pattern
|
||||||
|
|
||||||
// Args used for both PIN and Pattern options
|
// Args used for both PIN and Pattern options
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
|
@ -476,19 +476,20 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_SEND_TRANSFER)
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_SEND_TRANSFER)
|
||||||
|
|
||||||
when (securityLockSelected) {
|
when (securityLockSelected) {
|
||||||
0 -> { /* PIN */
|
0 -> { /* None */
|
||||||
|
startSendTransferOperation()
|
||||||
|
|
||||||
|
}
|
||||||
|
1 -> { /* PIN */
|
||||||
val pinFrag = PINSecurityLockDialog()
|
val pinFrag = PINSecurityLockDialog()
|
||||||
pinFrag.arguments = args
|
pinFrag.arguments = args
|
||||||
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
||||||
}
|
}
|
||||||
1 -> { /* Pattern */
|
else -> { /* Pattern */
|
||||||
val patternFrag = PatternSecurityLockDialog()
|
val patternFrag = PatternSecurityLockDialog()
|
||||||
patternFrag.arguments = args
|
patternFrag.arguments = args
|
||||||
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
||||||
}
|
}
|
||||||
else -> { /* None */
|
|
||||||
startSendTransferOperation()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,9 +104,9 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
||||||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||||
// Security Lock Options
|
// Security Lock Options
|
||||||
// 0 -> PIN
|
// 0 -> None
|
||||||
// 1 -> Pattern
|
// 1 -> PIN
|
||||||
// 2 -> None
|
// 2 -> Pattern
|
||||||
|
|
||||||
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
||||||
|
|
||||||
|
@ -229,9 +229,9 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
||||||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||||
// Security Lock Options
|
// Security Lock Options
|
||||||
// 0 -> PIN
|
// 0 -> None
|
||||||
// 1 -> Pattern
|
// 1 -> PIN
|
||||||
// 2 -> None
|
// 2 -> Pattern
|
||||||
|
|
||||||
// Args used for both PIN and Pattern options
|
// Args used for both PIN and Pattern options
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
|
@ -240,21 +240,21 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
||||||
|
|
||||||
return when (securityLockSelected) {
|
return when (securityLockSelected) {
|
||||||
0 -> { /* PIN */
|
0 -> { /* None */
|
||||||
|
false
|
||||||
|
}
|
||||||
|
1 -> { /* PIN */
|
||||||
val pinFrag = PINSecurityLockDialog()
|
val pinFrag = PINSecurityLockDialog()
|
||||||
pinFrag.arguments = args
|
pinFrag.arguments = args
|
||||||
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
1 -> { /* Pattern */
|
else -> { /* Pattern */
|
||||||
val patternFrag = PatternSecurityLockDialog()
|
val patternFrag = PatternSecurityLockDialog()
|
||||||
patternFrag.arguments = args
|
patternFrag.arguments = args
|
||||||
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> { /* None */
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,9 +271,9 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
||||||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||||
// Security Lock Options
|
// Security Lock Options
|
||||||
// 0 -> PIN
|
// 0 -> None
|
||||||
// 1 -> Pattern
|
// 1 -> PIN
|
||||||
// 2 -> None
|
// 2 -> Pattern
|
||||||
|
|
||||||
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
||||||
}
|
}
|
||||||
|
@ -293,23 +293,23 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1)
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1)
|
||||||
|
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> { /* PIN */
|
0 -> { /* None */
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||||
|
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0).apply() // 0 -> None
|
||||||
|
|
||||||
|
// Call this function to update the UI
|
||||||
|
onPINPatternChanged()
|
||||||
|
}
|
||||||
|
1 -> { /* PIN */
|
||||||
val pinFrag = PINSecurityLockDialog()
|
val pinFrag = PINSecurityLockDialog()
|
||||||
pinFrag.arguments = args
|
pinFrag.arguments = args
|
||||||
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
||||||
}
|
}
|
||||||
1 -> { /* Pattern */
|
else -> { /* Pattern */
|
||||||
val patternFrag = PatternSecurityLockDialog()
|
val patternFrag = PatternSecurityLockDialog()
|
||||||
patternFrag.arguments = args
|
patternFrag.arguments = args
|
||||||
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
||||||
}
|
}
|
||||||
else -> { /* None */
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
|
||||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 2).apply() // 2 -> None
|
|
||||||
|
|
||||||
// Call this function to update the UI
|
|
||||||
onPINPatternChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,15 @@ object Constants {
|
||||||
/** The minimum required length for a PIN number */
|
/** The minimum required length for a PIN number */
|
||||||
const val MIN_PIN_LENGTH = 6
|
const val MIN_PIN_LENGTH = 6
|
||||||
|
|
||||||
|
/** Salt used to securely generate the hash of the PIN/Pattern */
|
||||||
|
const val KEY_PIN_PATTERN_SALT = "key_pin_pattern_salt"
|
||||||
|
|
||||||
|
/** The user selected hashed PIN/Pattern */
|
||||||
|
const val KEY_HASHED_PIN_PATTERN = "key_hashed_pin_pattern"
|
||||||
|
|
||||||
|
/** Key used to store the user's selected Security Lock option */
|
||||||
|
const val KEY_SECURITY_LOCK_SELECTED = "key_security_lock_selected"
|
||||||
|
|
||||||
/** Name of the account passed to the faucet as the referrer */
|
/** Name of the account passed to the faucet as the referrer */
|
||||||
const val FAUCET_REFERRER = "agorise"
|
const val FAUCET_REFERRER = "agorise"
|
||||||
|
|
||||||
|
@ -25,9 +34,6 @@ object Constants {
|
||||||
/** Coingecko's API URL */
|
/** Coingecko's API URL */
|
||||||
const val COINGECKO_URL = "https://api.coingecko.com"
|
const val COINGECKO_URL = "https://api.coingecko.com"
|
||||||
|
|
||||||
/** The user selected encrypted PIN */
|
|
||||||
const val KEY_ENCRYPTED_PIN = "key_encrypted_pin"
|
|
||||||
|
|
||||||
/** The fee to send in every transfer (0.01%) */
|
/** The fee to send in every transfer (0.01%) */
|
||||||
const val FEE_PERCENTAGE = 0.0001
|
const val FEE_PERCENTAGE = 0.0001
|
||||||
|
|
||||||
|
@ -101,7 +107,4 @@ object Constants {
|
||||||
|
|
||||||
/** Name of the external storage folder used to save files like PDF and CSV exports and Backups **/
|
/** Name of the external storage folder used to save files like PDF and CSV exports and Backups **/
|
||||||
const val EXTERNAL_STORAGE_FOLDER = "BiTSy"
|
const val EXTERNAL_STORAGE_FOLDER = "BiTSy"
|
||||||
|
|
||||||
/** Key used to store the user's selected Security Lock option */
|
|
||||||
const val KEY_SECURITY_LOCK_SELECTED = "key_security_lock_selected"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,16 @@ package cy.agorise.bitsybitshareswallet.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
|
import android.util.Base64
|
||||||
|
|
||||||
import com.moldedbits.r2d2.R2d2
|
import com.moldedbits.r2d2.R2d2
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.security.NoSuchAlgorithmException
|
||||||
|
import java.security.SecureRandom
|
||||||
|
|
||||||
import javax.crypto.AEADBadTagException
|
import javax.crypto.AEADBadTagException
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that provides encryption/decryption support by using the key management framework provided
|
* Class that provides encryption/decryption support by using the key management framework provided
|
||||||
* by the KeyStore system.
|
* by the KeyStore system.
|
||||||
|
@ -68,5 +73,27 @@ object CryptoUtils {
|
||||||
val r2d2 = R2d2(context)
|
val r2d2 = R2d2(context)
|
||||||
return r2d2.decryptData(ciphertext)
|
return r2d2.decryptData(ciphertext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random salt string
|
||||||
|
*/
|
||||||
|
fun generateSalt(): String {
|
||||||
|
val salt = ByteArray(16)
|
||||||
|
SecureRandom().nextBytes(salt)
|
||||||
|
return Base64.encodeToString(salt, Base64.DEFAULT).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SHA-256 hash of the given string
|
||||||
|
*/
|
||||||
|
@Throws(NoSuchAlgorithmException::class)
|
||||||
|
fun createSHA256Hash(text: String): String {
|
||||||
|
val md = MessageDigest.getInstance("SHA-256")
|
||||||
|
|
||||||
|
md.update(text.toByteArray())
|
||||||
|
val digest = md.digest()
|
||||||
|
|
||||||
|
return Base64.encodeToString(digest, Base64.DEFAULT).trim()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/rootView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
android:paddingStart="@dimen/activity_horizontal_margin"
|
android:paddingStart="@dimen/activity_horizontal_margin"
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
<!-- Options used to populate the Security Lock Options Dialog in the Settings Screen -->
|
<!-- Options used to populate the Security Lock Options Dialog in the Settings Screen -->
|
||||||
<string-array name="security_lock_options">
|
<string-array name="security_lock_options">
|
||||||
|
<item>@string/text__none</item>
|
||||||
<item>@string/text__pin</item>
|
<item>@string/text__pin</item>
|
||||||
<item>@string/text__pattern</item>
|
<item>@string/text__pattern</item>
|
||||||
<item>@string/text__none</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue