diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java deleted file mode 100644 index d876aefd..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.zeapo.pwdstore; - -import android.net.Uri; - -import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; - -/** - * A single entry in password store. - */ -public class PasswordEntry { - - private static final String[] USERNAME_FIELDS = new String[]{"login", "username"}; - private final String password; - private final String username; - private final String totpSecret; - private final String hotpSecret; - private final Long hotpCounter; - private final String content; - private String extraContent; - private boolean isIncremented = false; - - public PasswordEntry(final ByteArrayOutputStream os) throws UnsupportedEncodingException { - this(os.toString("UTF-8")); - } - - public PasswordEntry(final String decryptedContent) { - final String[] passContent = decryptedContent.split("\n", 2); - content = decryptedContent; - password = passContent[0]; - totpSecret = findTotpSecret(content); - hotpSecret = findHotpSecret(content); - hotpCounter = findHotpCounter(content); - extraContent = findExtraContent(passContent); - username = findUsername(); - } - - public String getPassword() { - return password; - } - - public String getExtraContent() { - return extraContent; - } - - public String getUsername() { - return username; - } - - public String getTotpSecret() { - return totpSecret; - } - - public Long getHotpCounter() { - return hotpCounter; - } - - public String getHotpSecret() { - return hotpSecret; - } - - public boolean hasExtraContent() { - return extraContent.length() != 0; - } - - public boolean hasUsername() { - return username != null; - } - - public boolean hasTotp() { - return totpSecret != null; - } - - public boolean hasHotp() { - return hotpSecret != null && hotpCounter != null; - } - - public boolean hotpIsIncremented() { - return isIncremented; - } - - public void incrementHotp() { - for (String line : content.split("\n")) { - if (line.startsWith("otpauth://hotp/")) { - extraContent = extraContent.replaceFirst("counter=[0-9]+", "counter=" + Long.toString(hotpCounter + 1)); - isIncremented = true; - } - } - } - - private String findUsername() { - final String[] extraLines = extraContent.split("\n"); - for (String line : extraLines) { - for (String field : USERNAME_FIELDS) { - if (line.toLowerCase().startsWith(field + ":")) { - return line.split(": *", 2)[1]; - } - } - } - return null; - } - - private String findTotpSecret(String decryptedContent) { - for (String line : decryptedContent.split("\n")) { - if (line.startsWith("otpauth://totp/")) { - return Uri.parse(line).getQueryParameter("secret"); - } - } - return null; - } - - private String findHotpSecret(String decryptedContent) { - for (String line : decryptedContent.split("\n")) { - if (line.startsWith("otpauth://hotp/")) { - return Uri.parse(line).getQueryParameter("secret"); - } - } - return null; - } - - private Long findHotpCounter(String decryptedContent) { - for (String line : decryptedContent.split("\n")) { - if (line.startsWith("otpauth://hotp/")) { - return Long.parseLong(Uri.parse(line).getQueryParameter("counter")); - } - } - return null; - } - - private String findExtraContent(String[] passContent) { - String extraContent = passContent.length > 1 ? passContent[1] : ""; - // if there is a HOTP URI, we must return the extra content with the counter incremented - if (hasHotp()) { - return extraContent.replaceFirst("counter=[0-9]+", "counter=" + Long.toString(hotpCounter)); - } - return extraContent; - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt new file mode 100644 index 00000000..757681c5 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.kt @@ -0,0 +1,114 @@ +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) { + val password: String + val username: String? + val totpSecret: String? + val hotpSecret: String? + val hotpCounter: Long? + var extraContent: String? = null + private set + private var isIncremented = false + + @Throws(UnsupportedEncodingException::class) + constructor(os: ByteArrayOutputStream) : this(os.toString("UTF-8")) + + init { + val passContent = content.split("\n".toRegex(), 2).toTypedArray() + password = passContent[0] + totpSecret = findTotpSecret(content) + hotpSecret = findHotpSecret(content) + hotpCounter = findHotpCounter(content) + extraContent = findExtraContent(passContent) + username = findUsername() + } + + fun hasExtraContent(): Boolean { + return !extraContent.isNullOrEmpty() + } + + fun hasUsername(): Boolean { + 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 + } + } + } + + private fun findUsername(): String? { + val extraLines = extraContent!!.split("\n".toRegex()) + for (line in extraLines) { + for (field in USERNAME_FIELDS) { + if (line.toLowerCase().startsWith("$field:")) { + return line.split(": *".toRegex(), 2).toTypedArray()[1] + } + } + } + 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") + } + } + return null + } + + 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 { + 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=" + java.lang.Long.toString(hotpCounter!!)) + } else extraContent + } + + companion object { + + private val USERNAME_FIELDS = arrayOf("login", "username") + } +} 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 9828f43a..93fd7489 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PgpActivity.kt @@ -281,7 +281,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { crypto_username_show_label.visibility = View.VISIBLE crypto_copy_username.visibility = View.VISIBLE - crypto_copy_username.setOnClickListener { copyUsernameToClipBoard(entry.username) } + crypto_copy_username.setOnClickListener { copyUsernameToClipBoard(entry.username!!) } crypto_username_show.typeface = monoTypeface crypto_username_show.text = entry.username } else { @@ -494,8 +494,8 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { } private fun calculateHotp(entry: PasswordEntry) { - copyOtpToClipBoard(Otp.calculateCode(entry.hotpSecret, entry.hotpCounter + 1)) - crypto_otp_show.text = Otp.calculateCode(entry.hotpSecret, entry.hotpCounter + 1) + copyOtpToClipBoard(Otp.calculateCode(entry.hotpSecret, entry.hotpCounter!! + 1)) + crypto_otp_show.text = Otp.calculateCode(entry.hotpSecret, entry.hotpCounter!! + 1) crypto_extra_show.text = entry.extraContent }