aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
authorHarsh Shandilya <msfjarvis@gmail.com>2020-05-28 22:42:13 +0530
committerGitHub <noreply@github.com>2020-05-28 22:42:13 +0530
commite7463ec24c929860f0a7311b558f496919d54d20 (patch)
treeeb460f7e49e1a71acaceaec305e511bb4c0a9d45 /app/src/main/java
parentffcbabc2f4ef9766271be5335cec1869fe634646 (diff)
Remove HOTP/TOTP support (#806)
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt104
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt6
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/UserPreference.kt8
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt158
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Otp.java63
5 files changed, 7 insertions, 332 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt
index 32f671be..d9168d39 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt
@@ -4,26 +4,18 @@
*/
package com.zeapo.pwdstore
-import android.net.Uri
import java.io.ByteArrayOutputStream
import java.io.UnsupportedEncodingException
/**
* A single entry in password store.
*/
-class PasswordEntry(private val content: String) {
+class PasswordEntry(content: String) {
val password: String
val username: String?
- val digits: String
- val totpSecret: String?
- val totpPeriod: Long
- val totpAlgorithm: String
- val hotpSecret: String?
- val hotpCounter: Long?
var extraContent: String
private set
- private var isIncremented = false
@Throws(UnsupportedEncodingException::class)
constructor(os: ByteArrayOutputStream) : this(os.toString("UTF-8"))
@@ -31,12 +23,6 @@ class PasswordEntry(private val content: String) {
init {
val passContent = content.split("\n".toRegex(), 2).toTypedArray()
password = passContent[0]
- digits = findOtpDigits(content)
- totpSecret = findTotpSecret(content)
- totpPeriod = findTotpPeriod(content)
- totpAlgorithm = findTotpAlgorithm(content)
- hotpSecret = findHotpSecret(content)
- hotpCounter = findHotpCounter(content)
extraContent = findExtraContent(passContent)
username = findUsername()
}
@@ -49,27 +35,6 @@ class PasswordEntry(private val content: String) {
return username != null
}
- fun hasTotp(): Boolean {
- return totpSecret != null
- }
-
- fun hasHotp(): Boolean {
- return hotpSecret != null && hotpCounter != null
- }
-
- fun hotpIsIncremented(): Boolean {
- return isIncremented
- }
-
- fun incrementHotp() {
- content.split("\n".toRegex()).forEach { line ->
- if (line.startsWith("otpauth://hotp/")) {
- extraContent = extraContent.replaceFirst("counter=[0-9]+".toRegex(), "counter=${hotpCounter!! + 1}")
- isIncremented = true
- }
- }
- }
-
val extraContentWithoutUsername by lazy {
var usernameFound = false
extraContent.splitToSequence("\n").filter { line ->
@@ -93,73 +58,8 @@ class PasswordEntry(private val content: String) {
return null
}
- private fun findTotpSecret(decryptedContent: String): String? {
- decryptedContent.split("\n".toRegex()).forEach { line ->
- if (line.startsWith("otpauth://totp/")) {
- return Uri.parse(line).getQueryParameter("secret")
- }
- if (line.startsWith("totp:", ignoreCase = true)) {
- return line.split(": *".toRegex(), 2).toTypedArray()[1]
- }
- }
- return null
- }
-
- private fun findOtpDigits(decryptedContent: String): String {
- decryptedContent.split("\n".toRegex()).forEach { line ->
- if ((line.startsWith("otpauth://totp/") ||
- line.startsWith("otpauth://hotp/")) &&
- Uri.parse(line).getQueryParameter("digits") != null) {
- return Uri.parse(line).getQueryParameter("digits")!!
- }
- }
- return "6"
- }
-
- private fun findTotpPeriod(decryptedContent: String): Long {
- decryptedContent.split("\n".toRegex()).forEach { line ->
- if (line.startsWith("otpauth://totp/") &&
- Uri.parse(line).getQueryParameter("period") != null) {
- return java.lang.Long.parseLong(Uri.parse(line).getQueryParameter("period")!!)
- }
- }
- return 30
- }
-
- private fun findTotpAlgorithm(decryptedContent: String): String {
- decryptedContent.split("\n".toRegex()).forEach { line ->
- if (line.startsWith("otpauth://totp/") &&
- Uri.parse(line).getQueryParameter("algorithm") != null) {
- return Uri.parse(line).getQueryParameter("algorithm")!!
- }
- }
- return "sha1"
- }
-
- private fun findHotpSecret(decryptedContent: String): String? {
- decryptedContent.split("\n".toRegex()).forEach { line ->
- if (line.startsWith("otpauth://hotp/")) {
- return Uri.parse(line).getQueryParameter("secret")
- }
- }
- return null
- }
-
- private fun findHotpCounter(decryptedContent: String): Long? {
- decryptedContent.split("\n".toRegex()).forEach { line ->
- if (line.startsWith("otpauth://hotp/")) {
- return java.lang.Long.parseLong(Uri.parse(line).getQueryParameter("counter")!!)
- }
- }
- return null
- }
-
private fun findExtraContent(passContent: Array<String>): String {
- val extraContent = if (passContent.size > 1) passContent[1] else ""
- // if there is a HOTP URI, we must return the extra content with the counter incremented
- return if (hasHotp()) {
- extraContent.replaceFirst("counter=[0-9]+".toRegex(), "counter=" + (hotpCounter!!).toString())
- } else extraContent
+ return if (passContent.size > 1) passContent[1] else ""
}
companion object {
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
index 5857af74..45269145 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
@@ -636,16 +636,12 @@ class PasswordStore : AppCompatActivity() {
when (requestCode) {
// if we get here with a RESULT_OK then it's probably OK :)
BaseGitActivity.REQUEST_CLONE -> settings.edit { putBoolean("repository_initialized", true) }
- // if went from decrypt->edit and user saved changes or HOTP counter was
- // incremented, we need to commitChange
+ // if went from decrypt->edit and user saved changes, we need to commitChange
REQUEST_CODE_DECRYPT_AND_VERIFY -> {
if (data != null && data.getBooleanExtra("needCommit", false)) {
if (data.getStringExtra("OPERATION") == "EDIT") {
commitChange(resources.getString(R.string.git_commit_edit_text,
data.extras!!.getString("LONG_NAME")))
- } else {
- commitChange(resources.getString(R.string.git_commit_increment_text,
- data.extras!!.getString("LONG_NAME")))
}
}
refreshPasswordList()
diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
index 10665e38..41404716 100644
--- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
@@ -91,7 +91,6 @@ class UserPreference : AppCompatActivity() {
val sshKeyPreference = findPreference<Preference>("ssh_key")
val sshKeygenPreference = findPreference<Preference>("ssh_keygen")
clearSavedPassPreference = findPreference("clear_saved_pass")
- val clearHotpIncrementPreference = findPreference<Preference>("hotp_remember_clear_choice")
val viewSshKeyPreference = findPreference<Preference>("ssh_see_key")
val deleteRepoPreference = findPreference<Preference>("git_delete_repo")
val externalGitRepositoryPreference = findPreference<Preference>("git_external")
@@ -138,7 +137,6 @@ class UserPreference : AppCompatActivity() {
selectExternalGitRepositoryPreference?.summary = sharedPreferences.getString("git_external_repo", getString(R.string.no_repo_selected))
viewSshKeyPreference?.isVisible = sharedPreferences.getBoolean("use_generated_key", false)
deleteRepoPreference?.isVisible = !sharedPreferences.getBoolean("git_external", false)
- clearHotpIncrementPreference?.isVisible = sharedPreferences.getBoolean("hotp_remember_check", false)
clearClipboard20xPreference?.isVisible = sharedPreferences.getString("general_show_time", "45")?.toInt() != 0
val selectedKeys = (sharedPreferences.getStringSet("openpgp_key_ids_set", null)
?: HashSet()).toTypedArray()
@@ -197,12 +195,6 @@ class UserPreference : AppCompatActivity() {
true
}
- clearHotpIncrementPreference?.onPreferenceClickListener = ClickListener {
- sharedPreferences.edit { putBoolean("hotp_remember_check", false) }
- it.isVisible = false
- true
- }
-
openkeystoreIdPreference?.onPreferenceClickListener = ClickListener {
sharedPreferences.edit { putString("ssh_openkeystore_keyid", null) }
it.isVisible = false
diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
index 89399031..d9027230 100644
--- a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt
@@ -21,14 +21,12 @@ import android.text.InputType
import android.text.TextUtils
import android.text.format.DateUtils
import android.text.method.PasswordTransformationMethod
-import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.widget.Button
-import android.widget.CheckBox
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
@@ -41,7 +39,6 @@ import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.Timber.e
import com.github.ajalt.timberkt.Timber.i
import com.github.ajalt.timberkt.Timber.tag
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.ClipboardService
import com.zeapo.pwdstore.PasswordEntry
@@ -51,15 +48,11 @@ import com.zeapo.pwdstore.autofill.oreo.AutofillPreferences
import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.ui.dialogs.PasswordGeneratorDialogFragment
import com.zeapo.pwdstore.ui.dialogs.XkPasswordGeneratorDialogFragment
-import com.zeapo.pwdstore.utils.Otp
import kotlinx.android.synthetic.main.decrypt_layout.crypto_container_decrypt
-import kotlinx.android.synthetic.main.decrypt_layout.crypto_copy_otp
import kotlinx.android.synthetic.main.decrypt_layout.crypto_copy_username
import kotlinx.android.synthetic.main.decrypt_layout.crypto_extra_show
import kotlinx.android.synthetic.main.decrypt_layout.crypto_extra_show_layout
import kotlinx.android.synthetic.main.decrypt_layout.crypto_extra_toggle_show
-import kotlinx.android.synthetic.main.decrypt_layout.crypto_otp_show
-import kotlinx.android.synthetic.main.decrypt_layout.crypto_otp_show_label
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_category_decrypt
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_file
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_last_changed
@@ -93,7 +86,6 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.charset.Charset
-import java.util.Date
class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
private val clipboard by lazy { getSystemService<ClipboardManager>() }
@@ -284,7 +276,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
}
override fun onDestroy() {
- checkAndIncrementHotp()
super.onDestroy()
mServiceConnection?.unbindFromService()
}
@@ -304,23 +295,12 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
- android.R.id.home -> {
- if (passwordEntry?.hotpIsIncremented() == false) {
- setResult(RESULT_CANCELED)
- }
- finish()
- }
+ R.id.crypto_cancel_add, android.R.id.home -> finish()
R.id.copy_password -> copyPasswordToClipBoard()
R.id.share_password_as_plaintext -> shareAsPlaintext()
R.id.edit_password -> editPassword()
R.id.crypto_confirm_add -> encrypt()
R.id.crypto_confirm_add_and_copy -> encrypt(true)
- R.id.crypto_cancel_add -> {
- if (passwordEntry?.hotpIsIncremented() == false) {
- setResult(RESULT_CANCELED)
- }
- finish()
- }
else -> return super.onOptionsItemSelected(item)
}
return true
@@ -463,84 +443,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
}
}
- if (entry.hasTotp() || entry.hasHotp()) {
- crypto_extra_show_layout.visibility = View.VISIBLE
- crypto_extra_show.typeface = monoTypeface
- crypto_extra_show.text = entry.extraContent
-
- crypto_otp_show.visibility = View.VISIBLE
- crypto_otp_show_label.visibility = View.VISIBLE
- crypto_copy_otp.visibility = View.VISIBLE
-
- if (entry.hasTotp()) {
- crypto_copy_otp.setOnClickListener {
- copyOtpToClipBoard(
- Otp.calculateCode(
- entry.totpSecret,
- Date().time / (1000 * entry.totpPeriod),
- entry.totpAlgorithm,
- entry.digits)
- )
- }
- crypto_otp_show.text =
- Otp.calculateCode(
- entry.totpSecret,
- Date().time / (1000 * entry.totpPeriod),
- entry.totpAlgorithm,
- entry.digits)
- } else {
- // we only want to calculate and show HOTP if the user requests it
- crypto_copy_otp.setOnClickListener {
- if (settings.getBoolean("hotp_remember_check", false)) {
- if (settings.getBoolean("hotp_remember_choice", false)) {
- calculateAndCommitHotp(entry)
- } else {
- calculateHotp(entry)
- }
- } else {
- // show a dialog asking permission to update the HOTP counter in the entry
- val checkInflater = LayoutInflater.from(this@PgpActivity)
- val checkLayout = checkInflater.inflate(R.layout.otp_confirm_layout, null)
- val rememberCheck: CheckBox =
- checkLayout.findViewById(R.id.hotp_remember_checkbox)
- val dialogBuilder = MaterialAlertDialogBuilder(this@PgpActivity)
- dialogBuilder.setView(checkLayout)
- dialogBuilder.setMessage(R.string.dialog_update_body)
- .setCancelable(false)
- .setPositiveButton(R.string.dialog_update_positive) { _, _ ->
- run {
- calculateAndCommitHotp(entry)
- if (rememberCheck.isChecked) {
- settings.edit {
- putBoolean("hotp_remember_check", true)
- putBoolean("hotp_remember_choice", true)
- }
- }
- }
- }
- .setNegativeButton(R.string.dialog_update_negative) { _, _ ->
- run {
- calculateHotp(entry)
- settings.edit {
- putBoolean("hotp_remember_check", true)
- putBoolean("hotp_remember_choice", false)
- }
- }
- }
- val updateDialog = dialogBuilder.create()
- updateDialog.setTitle(R.string.dialog_update_title)
- updateDialog.show()
- }
- }
- crypto_otp_show.setText(R.string.hotp_pending)
- }
- crypto_otp_show.typeface = monoTypeface
- } else {
- crypto_otp_show.visibility = View.GONE
- crypto_otp_show_label.visibility = View.GONE
- crypto_copy_otp.visibility = View.GONE
- }
-
if (settings.getBoolean("copy_on_decrypt", true)) {
copyPasswordToClipBoard()
}
@@ -559,12 +461,9 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
* Encrypts the password and the extra content
*/
private fun encrypt(copy: Boolean = false) {
- // if HOTP was incremented, we leave fields as is; they have already been set
- if (intent.getStringExtra("OPERATION") != "INCREMENT") {
- editName = crypto_password_file_edit.text.toString().trim()
- editPass = crypto_password_edit.text.toString()
- editExtra = crypto_extra_edit.text.toString()
- }
+ editName = crypto_password_file_edit.text.toString().trim()
+ editPass = crypto_password_edit.text.toString()
+ editExtra = crypto_extra_edit.text.toString()
if (editName?.isEmpty() == true) {
showSnackbar(resources.getString(R.string.file_toast_text))
@@ -688,43 +587,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
}
/**
- * Writes updated HOTP counter to edit fields and encrypts
- */
- private fun checkAndIncrementHotp() {
- // we do not want to increment the HOTP counter if the user has edited the entry or has not
- // generated an HOTP code
- if (intent.getStringExtra("OPERATION") != "EDIT" && passwordEntry?.hotpIsIncremented() == true) {
- editName = name.trim()
- editPass = passwordEntry?.password
- editExtra = passwordEntry?.extraContent
-
- val data = Intent(this, PgpActivity::class.java)
- data.putExtra("OPERATION", "INCREMENT")
- data.putExtra("fromDecrypt", true)
- intent = data
- encrypt()
- }
- }
-
- private fun calculateHotp(entry: PasswordEntry) {
- copyOtpToClipBoard(Otp.calculateCode(entry.hotpSecret, entry.hotpCounter!! + 1, "sha1", entry.digits))
- crypto_otp_show.text = Otp.calculateCode(entry.hotpSecret, entry.hotpCounter + 1, "sha1", entry.digits)
- crypto_extra_show.text = entry.extraContent
- }
-
- private fun calculateAndCommitHotp(entry: PasswordEntry) {
- calculateHotp(entry)
- entry.incrementHotp()
- // we must set the result before encrypt() is called, since in
- // some cases it is called during the finish() sequence
- val returnIntent = Intent()
- returnIntent.putExtra("NAME", name.trim())
- returnIntent.putExtra("OPERATION", "INCREMENT")
- returnIntent.putExtra("needCommit", true)
- setResult(RESULT_OK, returnIntent)
- }
-
- /**
* Get the Key ids from OpenKeychain
*/
private fun getKeyIds(receivedIntent: Intent? = null) {
@@ -861,13 +723,6 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
showSnackbar(resources.getString(R.string.clipboard_username_toast_text))
}
- private fun copyOtpToClipBoard(code: String) {
- val clipboard = clipboard ?: return
- val clip = ClipData.newPlainText("pgp_handler_result_pm", code)
- clipboard.setPrimaryClip(clip)
- showSnackbar(resources.getString(R.string.clipboard_otp_toast_text))
- }
-
private fun shareAsPlaintext() {
if (findViewById<View>(R.id.share_password_as_plaintext) == null)
return
@@ -957,13 +812,8 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
fun doOnPostExecute() {
if (skip) return
- checkAndIncrementHotp()
if (crypto_password_show != null) {
- // clear password; if decrypt changed to encrypt layout via edit button, no need
- if (passwordEntry?.hotpIsIncremented() == false) {
- setResult(RESULT_CANCELED)
- }
passwordEntry = null
crypto_password_show.text = ""
crypto_extra_show.text = ""
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java b/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java
deleted file mode 100644
index 07f73423..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Otp.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.utils;
-
-import org.apache.commons.codec.binary.Base32;
-
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-
-import timber.log.Timber;
-
-public class Otp {
-
- private static final Base32 BASE_32 = new Base32();
-
- private Otp() {
- }
-
- public static String calculateCode(
- String secret, long counter, String algorithm, String digits) {
- String[] steam = {
- "2", "3", "4", "5", "6", "7", "8", "9", "B", "C", "D", "F", "G", "H", "J", "K", "M",
- "N", "P", "Q", "R", "T", "V", "W", "X", "Y"
- };
- String ALGORITHM = "Hmac" + algorithm.toUpperCase();
- SecretKeySpec signingKey = new SecretKeySpec(BASE_32.decode(secret), ALGORITHM);
-
- Mac mac;
- try {
- mac = Mac.getInstance(ALGORITHM);
- mac.init(signingKey);
- } catch (NoSuchAlgorithmException e) {
- Timber.tag("TOTP").e(e, "%s unavailable - should never happen", ALGORITHM);
- return null;
- } catch (InvalidKeyException e) {
- Timber.tag("TOTP").e(e, "Key is malformed");
- return null;
- }
-
- byte[] digest = mac.doFinal(ByteBuffer.allocate(8).putLong(counter).array());
- int offset = digest[digest.length - 1] & 0xf;
- byte[] code = Arrays.copyOfRange(digest, offset, offset + 4);
- code[0] = (byte) (0x7f & code[0]);
- String strCode = new BigInteger(code).toString();
- if (digits.equals("s")) {
- StringBuilder output = new StringBuilder();
- int bigInt = new BigInteger(code).intValue();
- for (int i = 0; i != 5; i++) {
- output.append(steam[bigInt % 26]);
- bigInt /= 26;
- }
- return output.toString();
- } else return strCode.substring(strCode.length() - Integer.parseInt(digits));
- }
-}