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"> + + + + - - - -