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
|
||||
PreferenceManager.getDefaultSharedPreferences(context!!)
|
||||
.edit()
|
||||
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPIN)
|
||||
.putString(Constants.KEY_HASHED_PIN_PATTERN, encryptedPIN)
|
||||
.apply()
|
||||
|
||||
// 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 */
|
||||
protected var mDisposables = CompositeDisposable()
|
||||
|
||||
/** Current encrypted version of the PIN/Pattern */
|
||||
protected var currentEncryptedPINPattern: String? = null
|
||||
/** Current hashed version of the salt + PIN/Pattern */
|
||||
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?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
onAttachToParentFragment(parentFragment)
|
||||
|
||||
currentEncryptedPINPattern = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(Constants.KEY_ENCRYPTED_PIN, "")?.trim()
|
||||
currentHashedPINPattern = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(Constants.KEY_HASHED_PIN_PATTERN, "")
|
||||
|
||||
currentPINPatternSalt = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(Constants.KEY_PIN_PATTERN_SALT, "")
|
||||
|
||||
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.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||
import cy.agorise.bitsybitshareswallet.utils.hideKeyboard
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.dialog_pin_security_lock.*
|
||||
|
||||
|
@ -39,16 +40,20 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
var handled = false
|
||||
if (actionId == EditorInfo.IME_ACTION_GO) {
|
||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||
// The user just wants to verify the current encrypted PIN/Pattern
|
||||
val encryptedPIN = CryptoUtils.encrypt(v.context, v.text.toString()).trim()
|
||||
// The user just wants to verify the current hashed PIN/Pattern
|
||||
val pin = v.text.toString().trim()
|
||||
val hashedPIN = CryptoUtils.createSHA256Hash(currentPINPatternSalt + pin)
|
||||
|
||||
if (encryptedPIN == currentEncryptedPINPattern) {
|
||||
if (hashedPIN == currentHashedPINPattern) {
|
||||
// PIN is correct, proceed
|
||||
tietPIN.hideKeyboard()
|
||||
rootView.requestFocus()
|
||||
dismiss()
|
||||
mCallback?.onPINPatternEntered(actionIdentifier)
|
||||
} else {
|
||||
tilPIN.error = getString(R.string.error__wrong_pin)
|
||||
}
|
||||
|
||||
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
||||
// The user is trying to create a new PIN
|
||||
if (v.text.toString().trim().length >= Constants.MIN_PIN_LENGTH) {
|
||||
|
@ -62,12 +67,14 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
if (pinConfirm != newPIN) {
|
||||
tvTitle.text = getString(R.string.title__pins_dont_match)
|
||||
} 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()
|
||||
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPIN)
|
||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0).apply() // 0 -> PIN
|
||||
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPIN)
|
||||
.putString(Constants.KEY_PIN_PATTERN_SALT, salt)
|
||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 1).apply() // 1 -> PIN
|
||||
|
||||
dismiss()
|
||||
mCallback?.onPINPatternChanged()
|
||||
|
|
|
@ -64,8 +64,9 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
override fun onComplete(pattern: List<PatternLockView.Dot>) {
|
||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||
context?.let {
|
||||
val encryptedPattern = CryptoUtils.encrypt(it, getStringPattern(pattern)).trim()
|
||||
if (encryptedPattern == currentEncryptedPINPattern) {
|
||||
val hashedPattern = CryptoUtils.createSHA256Hash(currentPINPatternSalt +
|
||||
getStringPattern(pattern))
|
||||
if (hashedPattern == currentHashedPINPattern) {
|
||||
// Pattern is correct, proceed
|
||||
dismiss()
|
||||
mCallback?.onPINPatternEntered(actionIdentifier)
|
||||
|
@ -102,12 +103,14 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
btnNext.isEnabled = true
|
||||
btnNext.setOnClickListener {
|
||||
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
|
||||
PreferenceManager.getDefaultSharedPreferences(it).edit()
|
||||
.putString(Constants.KEY_ENCRYPTED_PIN, encryptedPattern)
|
||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 1).apply() // 1 -> Pattern
|
||||
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPattern)
|
||||
.putString(Constants.KEY_PIN_PATTERN_SALT, salt)
|
||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 2).apply() // 2 -> Pattern
|
||||
|
||||
dismiss()
|
||||
mCallback?.onPINPatternChanged()
|
||||
|
|
|
@ -465,9 +465,9 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||
// Security Lock Options
|
||||
// 0 -> PIN
|
||||
// 1 -> Pattern
|
||||
// 2 -> None
|
||||
// 0 -> None
|
||||
// 1 -> PIN
|
||||
// 2 -> Pattern
|
||||
|
||||
// Args used for both PIN and Pattern options
|
||||
val args = Bundle()
|
||||
|
@ -476,19 +476,20 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_SEND_TRANSFER)
|
||||
|
||||
when (securityLockSelected) {
|
||||
0 -> { /* PIN */
|
||||
0 -> { /* None */
|
||||
startSendTransferOperation()
|
||||
|
||||
}
|
||||
1 -> { /* PIN */
|
||||
val pinFrag = PINSecurityLockDialog()
|
||||
pinFrag.arguments = args
|
||||
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
||||
}
|
||||
1 -> { /* Pattern */
|
||||
else -> { /* Pattern */
|
||||
val patternFrag = PatternSecurityLockDialog()
|
||||
patternFrag.arguments = args
|
||||
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)
|
||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||
// Security Lock Options
|
||||
// 0 -> PIN
|
||||
// 1 -> Pattern
|
||||
// 2 -> None
|
||||
// 0 -> None
|
||||
// 1 -> PIN
|
||||
// 2 -> Pattern
|
||||
|
||||
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)
|
||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||
// Security Lock Options
|
||||
// 0 -> PIN
|
||||
// 1 -> Pattern
|
||||
// 2 -> None
|
||||
// 0 -> None
|
||||
// 1 -> PIN
|
||||
// 2 -> Pattern
|
||||
|
||||
// Args used for both PIN and Pattern options
|
||||
val args = Bundle()
|
||||
|
@ -240,21 +240,21 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
|||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
||||
|
||||
return when (securityLockSelected) {
|
||||
0 -> { /* PIN */
|
||||
0 -> { /* None */
|
||||
false
|
||||
}
|
||||
1 -> { /* PIN */
|
||||
val pinFrag = PINSecurityLockDialog()
|
||||
pinFrag.arguments = args
|
||||
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
||||
true
|
||||
}
|
||||
1 -> { /* Pattern */
|
||||
else -> { /* Pattern */
|
||||
val patternFrag = PatternSecurityLockDialog()
|
||||
patternFrag.arguments = args
|
||||
patternFrag.show(childFragmentManager, "pattern_security_lock_tag")
|
||||
true
|
||||
}
|
||||
else -> { /* None */
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,9 +271,9 @@ class SettingsFragment : Fragment(), ServiceConnection, BaseSecurityLockDialog.O
|
|||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||
// Security Lock Options
|
||||
// 0 -> PIN
|
||||
// 1 -> Pattern
|
||||
// 2 -> None
|
||||
// 0 -> None
|
||||
// 1 -> PIN
|
||||
// 2 -> Pattern
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
pinFrag.arguments = args
|
||||
pinFrag.show(childFragmentManager, "pin_security_lock_tag")
|
||||
}
|
||||
1 -> { /* Pattern */
|
||||
else -> { /* Pattern */
|
||||
val patternFrag = PatternSecurityLockDialog()
|
||||
patternFrag.arguments = args
|
||||
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 */
|
||||
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 */
|
||||
const val FAUCET_REFERRER = "agorise"
|
||||
|
||||
|
@ -25,9 +34,6 @@ object Constants {
|
|||
/** Coingecko's API URL */
|
||||
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%) */
|
||||
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 **/
|
||||
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.preference.PreferenceManager
|
||||
import android.util.Base64
|
||||
|
||||
import com.moldedbits.r2d2.R2d2
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.SecureRandom
|
||||
|
||||
import javax.crypto.AEADBadTagException
|
||||
|
||||
|
||||
/**
|
||||
* Class that provides encryption/decryption support by using the key management framework provided
|
||||
* by the KeyStore system.
|
||||
|
@ -68,5 +73,27 @@ object CryptoUtils {
|
|||
val r2d2 = R2d2(context)
|
||||
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:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rootView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusableInTouchMode="true"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingStart="@dimen/activity_horizontal_margin"
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
<!-- Options used to populate the Security Lock Options Dialog in the Settings Screen -->
|
||||
<string-array name="security_lock_options">
|
||||
<item>@string/text__none</item>
|
||||
<item>@string/text__pin</item>
|
||||
<item>@string/text__pattern</item>
|
||||
<item>@string/text__none</item>
|
||||
</string-array>
|
||||
</resources>
|
Loading…
Reference in a new issue