Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
Reintroduce TOTP support (#890)
Browse files Browse the repository at this point in the history
Co-authored-by: Fabian Henneke <fabian@henneke.me>
  • Loading branch information
Harsh Shandilya and fmeum authored Jun 29, 2020
1 parent 56c301d commit 063c1a1
Show file tree
Hide file tree
Showing 23 changed files with 575 additions and 163 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ All notable changes to this project will be documented in this file.
- Folder names that were very long did not look right
- Error message for wrong SSH/HTTPS password now looks cleaner

### Added

- TOTP support is reintroduced by popular demand. HOTP continues to be unsupported and heavily discouraged.

## [1.9.1] - 2020-06-28

### Fixed
Expand Down
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ dependencies {
implementation deps.kotlin.coroutines.android
implementation deps.kotlin.coroutines.core

implementation deps.third_party.commons_codec
implementation deps.third_party.fastscroll
implementation(deps.third_party.jgit) {
exclude group: 'org.apache.httpcomponents', module: 'httpclient'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.junit.Test
import kotlin.test.assertEquals

class UriTotpFinderTest {

private val totpFinder = UriTotpFinder()

@Test
fun findSecret() {
assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret(TOTP_URI))
assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret("name\npassword\ntotp: HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"))
}

@Test
fun findDigits() {
assertEquals("12", totpFinder.findDigits(TOTP_URI))
}

@Test
fun findPeriod() {
assertEquals(25, totpFinder.findPeriod(TOTP_URI))
}

@Test
fun findAlgorithm() {
assertEquals("SHA256", totpFinder.findAlgorithm(TOTP_URI))
}

companion object {
const val TOTP_URI = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA256&digits=12&period=25"
}
}
78 changes: 0 additions & 78 deletions app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import com.github.ajalt.timberkt.Timber.tag
import com.github.ajalt.timberkt.e
import com.github.ajalt.timberkt.i
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.model.PasswordEntry
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.splitLines
import kotlinx.coroutines.CoroutineScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import androidx.annotation.RequiresApi
import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.Timber.tag
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.model.PasswordEntry
import com.zeapo.pwdstore.utils.PasswordRepository
import java.io.File
import java.security.MessageDigest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import android.widget.Toast
import androidx.annotation.RequiresApi
import com.github.ajalt.timberkt.d
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.autofill.oreo.AutofillAction
import com.zeapo.pwdstore.autofill.oreo.AutofillPreferences
import com.zeapo.pwdstore.autofill.oreo.Credentials
import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.autofill.oreo.FillableForm
import com.zeapo.pwdstore.model.PasswordEntry
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
Expand Down
10 changes: 8 additions & 2 deletions app/src/main/java/com/zeapo/pwdstore/crypto/BasePgpActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import android.view.WindowManager
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.CallSuper
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.Timber.tag
Expand Down Expand Up @@ -163,6 +164,7 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou

return DateUtils.getRelativeTimeSpanString(this, timeStamp, true)
}

/**
* Base handling of OpenKeychain errors based on the error contained in [result]. Subclasses
* can use this when they want to default to sane error handling.
Expand Down Expand Up @@ -190,12 +192,16 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou
* Copies provided [text] to the clipboard. Shows a [Snackbar] which can be disabled by passing
* [showSnackbar] as false.
*/
fun copyTextToClipboard(text: String?, showSnackbar: Boolean = true) {
fun copyTextToClipboard(
text: String?,
showSnackbar: Boolean = true,
@StringRes snackbarTextRes: Int = R.string.clipboard_copied_text
) {
val clipboard = clipboard ?: return
val clip = ClipData.newPlainText("pgp_handler_result_pm", text)
clipboard.setPrimaryClip(clip)
if (showSnackbar) {
snackbar(message = resources.getString(R.string.clipboard_copied_text))
snackbar(message = resources.getString(snackbarTextRes))
}
}

Expand Down
49 changes: 41 additions & 8 deletions app/src/main/java/com/zeapo/pwdstore/crypto/DecryptActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,23 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo
import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult
import androidx.lifecycle.lifecycleScope
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.DecryptLayoutBinding
import com.zeapo.pwdstore.model.PasswordEntry
import com.zeapo.pwdstore.utils.Otp
import com.zeapo.pwdstore.utils.viewBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.msfjarvis.openpgpktx.util.OpenPgpApi
import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
import org.openintents.openpgp.IOpenPgpService2
import java.io.ByteArrayOutputStream
import java.io.File
import java.util.Date
import kotlin.time.ExperimentalTime
import kotlin.time.seconds

class DecryptActivity : BasePgpActivity(), OpenPgpServiceConnection.OnBound {
private val binding by viewBinding(DecryptLayoutBinding::inflate)
Expand Down Expand Up @@ -125,6 +131,7 @@ class DecryptActivity : BasePgpActivity(), OpenPgpServiceConnection.OnBound {
startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_plaintext_password_to)))
}

@OptIn(ExperimentalTime::class)
private fun decryptAndVerify(receivedIntent: Intent? = null) {
if (api == null) {
bindToOpenKeychain(this, openKeychainResult)
Expand Down Expand Up @@ -163,14 +170,16 @@ class DecryptActivity : BasePgpActivity(), OpenPgpServiceConnection.OnBound {
}

if (entry.hasExtraContent()) {
extraContentContainer.visibility = View.VISIBLE
extraContent.typeface = monoTypeface
extraContent.setText(entry.extraContentWithoutUsername)
if (!showExtraContent) {
extraContent.transformationMethod = PasswordTransformationMethod.getInstance()
if (entry.extraContentWithoutAuthData.isNotEmpty()) {
extraContentContainer.visibility = View.VISIBLE
extraContent.typeface = monoTypeface
extraContent.setText(entry.extraContentWithoutAuthData)
if (!showExtraContent) {
extraContent.transformationMethod = PasswordTransformationMethod.getInstance()
}
extraContentContainer.setOnClickListener { copyTextToClipboard(entry.extraContentWithoutAuthData) }
extraContent.setOnClickListener { copyTextToClipboard(entry.extraContentWithoutAuthData) }
}
extraContentContainer.setOnClickListener { copyTextToClipboard(entry.extraContentWithoutUsername) }
extraContent.setOnClickListener { copyTextToClipboard(entry.extraContentWithoutUsername) }

if (entry.hasUsername()) {
usernameText.typeface = monoTypeface
Expand All @@ -180,6 +189,30 @@ class DecryptActivity : BasePgpActivity(), OpenPgpServiceConnection.OnBound {
} else {
usernameTextContainer.visibility = View.GONE
}

if (entry.hasTotp()) {
otpTextContainer.visibility = View.VISIBLE
otpTextContainer.setEndIconOnClickListener {
copyTextToClipboard(
otpText.text.toString(),
snackbarTextRes = R.string.clipboard_otp_copied_text
)
}
launch(Dispatchers.IO) {
repeat(Int.MAX_VALUE) {
val code = Otp.calculateCode(
entry.totpSecret!!,
Date().time / (1000 * entry.totpPeriod),
entry.totpAlgorithm,
entry.digits
) ?: "Error"
withContext(Dispatchers.Main) {
otpText.setText(code)
}
delay(30.seconds)
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.lifecycleScope
import com.github.ajalt.timberkt.e
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.utils.isInsideRepository
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.autofill.oreo.AutofillPreferences
import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.databinding.PasswordCreationActivityBinding
import com.zeapo.pwdstore.model.PasswordEntry
import com.zeapo.pwdstore.ui.dialogs.PasswordGeneratorDialogFragment
import com.zeapo.pwdstore.ui.dialogs.XkPasswordGeneratorDialogFragment
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.commitChange
import com.zeapo.pwdstore.utils.isInsideRepository
import com.zeapo.pwdstore.utils.snackbar
import com.zeapo.pwdstore.utils.viewBinding
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -108,7 +108,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
// input lag.
if (username != null) {
filename.setText(username)
extraContent.setText(entry.extraContentWithoutUsername)
extraContent.setText(entry.extraContentWithoutAuthData)
}
}
updateEncryptUsernameState()
Expand Down
Loading

0 comments on commit 063c1a1

Please sign in to comment.