From eea0e68dda1eb7248c6d458f52baeedb318b466a Mon Sep 17 00:00:00 2001 From: Joel Beckmeyer Date: Tue, 25 Sep 2018 13:45:54 -0400 Subject: Display HOTP code if password contains HOTP secret, unify HOTP and TOTP code (#413) * Display HOTP code if password contains HOTP secret, unify HOTP and TOTP code * Add ability to show HOTP instead of showing every decrypt * Fix off by 1 error * fix return intent logic so that edits and HOTP increments are properly committed * fix linting errors * Fix broken logic for case when a password is created * add ability to choose if password entry will be updated on HOTP code calculation --- .../java/com/zeapo/pwdstore/PasswordEntry.java | 69 ++++++++++++++++++++-- 1 file changed, 64 insertions(+), 5 deletions(-) (limited to 'app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java') diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java index ba689fe9..590a779e 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordEntry.java @@ -12,10 +12,14 @@ public class PasswordEntry { private static final String[] USERNAME_FIELDS = new String[]{"login", "username"}; - private final String extraContent; + private String extraContent; 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 boolean isIncremented = false; public PasswordEntry(final ByteArrayOutputStream os) throws UnsupportedEncodingException { this(os.toString("UTF-8")); @@ -23,11 +27,14 @@ public class PasswordEntry { public PasswordEntry(final String decryptedContent) { final String[] passContent = decryptedContent.split("\n", 2); + content = decryptedContent; password = passContent[0]; - extraContent = passContent.length > 1 ? passContent[1] : ""; + totpSecret = findTotpSecret(content); + hotpSecret = findHotpSecret(content); + hotpCounter = findHotpCounter(content); + extraContent = findExtraContent(passContent); username = findUsername(); - totpSecret = findTotpSecret(decryptedContent); - } + } public String getPassword() { return password; @@ -45,6 +52,14 @@ public class PasswordEntry { return totpSecret; } + public Long getHotpCounter() { + return hotpCounter; + } + + public String getHotpSecret() { + return hotpSecret; + } + public boolean hasExtraContent() { return extraContent.length() != 0; } @@ -53,7 +68,24 @@ public class PasswordEntry { return username != null; } - public boolean hasTotp() { return totpSecret != 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"); @@ -75,4 +107,31 @@ public class PasswordEntry { } 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; + } } -- cgit v1.2.3