diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
index 4cf16f937..58555763d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
@@ -40,6 +40,7 @@ import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
+import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
@@ -355,7 +356,10 @@ class EntryEditActivity : LockingHideActivity(),
R.id.menu_add_otp -> {
SetOTPDialogFragment().apply {
createOTPElementListener = { otpElement ->
- // TODO Add custom field
+ val otpField = OtpEntryFields.buildOtpField(otpElement,
+ mEntry?.title, mEntry?.username)
+ entryEditContentsView?.addNewCustomField(otpField.name, otpField.protectedValue)
+
}
}.show(supportFragmentManager, "addOTPDialog")
return true
diff --git a/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt b/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt
index 1e8750d16..7eccfb748 100644
--- a/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt
@@ -25,6 +25,7 @@ import android.util.Log
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.model.Field
import com.kunzisoft.keepass.otp.TokenCalculator.*
+import java.lang.StringBuilder
import java.util.*
import java.util.regex.Pattern
@@ -161,48 +162,50 @@ object OtpEntryFields {
otpElement.period = stepParam.toInt()
val algorithmParam = uri.getQueryParameter(ALGORITHM_URL_PARAM)
- if (algorithmParam != null && algorithmParam.isNotEmpty())
- otpElement.algorithm = HashAlgorithm.valueOf(algorithmParam.toUpperCase(Locale.ENGLISH))
+ if (algorithmParam != null && algorithmParam.isNotEmpty()) {
+ otpElement.algorithm = HashAlgorithm.fromString(algorithmParam)
+ }
return true
}
return false
}
- private fun buildOtpUri(otpElement: OtpElement): Uri {
+ private fun buildOtpUri(otpElement: OtpElement, title: String?, username: String?): Uri {
val counterOrPeriodLabel: String
val counterOrPeriodValue: String
- val otpAuthority = when (otpElement.type) {
+ val otpAuthority: String
+
+ when (otpElement.type) {
OtpType.TOTP -> {
counterOrPeriodLabel = PERIOD_URL_PARAM
counterOrPeriodValue = otpElement.period.toString()
- TOTP_AUTHORITY
+ otpAuthority = TOTP_AUTHORITY
}
else -> {
counterOrPeriodLabel = COUNTER_URL_PARAM
counterOrPeriodValue = otpElement.counter.toString()
- HOTP_AUTHORITY
+ otpAuthority = HOTP_AUTHORITY
}
}
- val accountName = "OTP"
- val issuer = "None"
- val otpLabel = "$issuer:$accountName"
- val otpSecret = otpElement.getBase32Secret()
- val otpDigits = otpElement.digits
- val otpIssuer = otpElement.issuer
- val otpAlgorithm = otpElement.algorithm.toString()
-
- val uriString = "otpauth://$otpAuthority/$otpLabel" +
- "?$SECRET_URL_PARAM=$otpSecret" +
+ val issuer = if (otpElement.issuer.isEmpty()) {
+ if (title == null || title.isEmpty())"None" else title
+ } else {
+ otpElement.issuer
+ }
+ val accountName = if (username == null || username.isEmpty()) "OTP" else username
+ val uriString = StringBuilder("otpauth://$otpAuthority/$issuer:$accountName" +
+ "?$SECRET_URL_PARAM=${otpElement.getBase32Secret()}" +
"&$counterOrPeriodLabel=$counterOrPeriodValue" +
- "&$DIGITS_URL_PARAM=$otpDigits" +
- "&$ISSUER_URL_PARAM=$otpIssuer" +
- "&$ALGORITHM_URL_PARAM=$otpAlgorithm" +
- "\n"
+ "&$DIGITS_URL_PARAM=${otpElement.digits}" +
+ "&$ISSUER_URL_PARAM=$issuer")
+ if (otpElement.tokenType == OtpTokenType.STEAM) {
+ uriString.append("&$ENCODER_URL_PARAM=${otpElement.tokenType}")
+ } else {
+ uriString.append("&$ALGORITHM_URL_PARAM=${otpElement.algorithm}")
+ }
- // TODO steam with ENCODER_URL_PARAM
-
- return Uri.parse(uriString)
+ return Uri.parse(uriString.toString())
}
private fun parseTOTPKeyValues(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
@@ -296,8 +299,9 @@ object OtpEntryFields {
/**
* Build Otp field from an OtpElement
*/
- fun buildOtpField(otpElement: OtpElement): Field {
- return Field(OTP_FIELD, ProtectedString(true, buildOtpUri(otpElement).toString()))
+ fun buildOtpField(otpElement: OtpElement, title: String?, username: String?): Field {
+ return Field(OTP_FIELD, ProtectedString(true,
+ buildOtpUri(otpElement, title, username).toString()))
}
/**
diff --git a/app/src/main/java/com/kunzisoft/keepass/otp/TokenCalculator.java b/app/src/main/java/com/kunzisoft/keepass/otp/TokenCalculator.java
index 960aff72f..54f2e5cdc 100644
--- a/app/src/main/java/com/kunzisoft/keepass/otp/TokenCalculator.java
+++ b/app/src/main/java/com/kunzisoft/keepass/otp/TokenCalculator.java
@@ -42,7 +42,16 @@ public class TokenCalculator {
};
public enum HashAlgorithm {
- SHA1, SHA256, SHA512
+ SHA1, SHA256, SHA512;
+
+ static HashAlgorithm fromString(String hashString) {
+ String hash = hashString.replace("[^a-zA-Z0-9]", "").toUpperCase();
+ try {
+ return valueOf(hash);
+ } catch (Exception e) {
+ return SHA1;
+ }
+ }
}
public static final HashAlgorithm DEFAULT_ALGORITHM = HashAlgorithm.SHA1;
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt
index bb84b883d..b8c25ceab 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt
@@ -168,6 +168,7 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
/**
* Add a new view to fill in the information of the customized field
*/
+ // TODO replace if name already exists
fun addNewCustomField(name: String = "", value: ProtectedString = ProtectedString(false, "")) {
val entryEditCustomField = EntryEditCustomField(context).apply {
setData(name, value)
diff --git a/app/src/main/res/layout/fragment_set_otp.xml b/app/src/main/res/layout/fragment_set_otp.xml
index b578a3690..326a01db1 100644
--- a/app/src/main/res/layout/fragment_set_otp.xml
+++ b/app/src/main/res/layout/fragment_set_otp.xml
@@ -88,6 +88,18 @@
android:layout_margin="@dimen/default_margin"
android:orientation="vertical">
+
+
+
+
-
-
-
-