mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Build OTP url
This commit is contained in:
@@ -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_CREATE_ENTRY_TASK
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||||
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
|
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
|
||||||
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
@@ -355,7 +356,10 @@ class EntryEditActivity : LockingHideActivity(),
|
|||||||
R.id.menu_add_otp -> {
|
R.id.menu_add_otp -> {
|
||||||
SetOTPDialogFragment().apply {
|
SetOTPDialogFragment().apply {
|
||||||
createOTPElementListener = { otpElement ->
|
createOTPElementListener = { otpElement ->
|
||||||
// TODO Add custom field
|
val otpField = OtpEntryFields.buildOtpField(otpElement,
|
||||||
|
mEntry?.title, mEntry?.username)
|
||||||
|
entryEditContentsView?.addNewCustomField(otpField.name, otpField.protectedValue)
|
||||||
|
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, "addOTPDialog")
|
}.show(supportFragmentManager, "addOTPDialog")
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.util.Log
|
|||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
import com.kunzisoft.keepass.model.Field
|
import com.kunzisoft.keepass.model.Field
|
||||||
import com.kunzisoft.keepass.otp.TokenCalculator.*
|
import com.kunzisoft.keepass.otp.TokenCalculator.*
|
||||||
|
import java.lang.StringBuilder
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@@ -161,48 +162,50 @@ object OtpEntryFields {
|
|||||||
otpElement.period = stepParam.toInt()
|
otpElement.period = stepParam.toInt()
|
||||||
|
|
||||||
val algorithmParam = uri.getQueryParameter(ALGORITHM_URL_PARAM)
|
val algorithmParam = uri.getQueryParameter(ALGORITHM_URL_PARAM)
|
||||||
if (algorithmParam != null && algorithmParam.isNotEmpty())
|
if (algorithmParam != null && algorithmParam.isNotEmpty()) {
|
||||||
otpElement.algorithm = HashAlgorithm.valueOf(algorithmParam.toUpperCase(Locale.ENGLISH))
|
otpElement.algorithm = HashAlgorithm.fromString(algorithmParam)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildOtpUri(otpElement: OtpElement): Uri {
|
private fun buildOtpUri(otpElement: OtpElement, title: String?, username: String?): Uri {
|
||||||
val counterOrPeriodLabel: String
|
val counterOrPeriodLabel: String
|
||||||
val counterOrPeriodValue: String
|
val counterOrPeriodValue: String
|
||||||
val otpAuthority = when (otpElement.type) {
|
val otpAuthority: String
|
||||||
|
|
||||||
|
when (otpElement.type) {
|
||||||
OtpType.TOTP -> {
|
OtpType.TOTP -> {
|
||||||
counterOrPeriodLabel = PERIOD_URL_PARAM
|
counterOrPeriodLabel = PERIOD_URL_PARAM
|
||||||
counterOrPeriodValue = otpElement.period.toString()
|
counterOrPeriodValue = otpElement.period.toString()
|
||||||
TOTP_AUTHORITY
|
otpAuthority = TOTP_AUTHORITY
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
counterOrPeriodLabel = COUNTER_URL_PARAM
|
counterOrPeriodLabel = COUNTER_URL_PARAM
|
||||||
counterOrPeriodValue = otpElement.counter.toString()
|
counterOrPeriodValue = otpElement.counter.toString()
|
||||||
HOTP_AUTHORITY
|
otpAuthority = HOTP_AUTHORITY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val accountName = "OTP"
|
val issuer = if (otpElement.issuer.isEmpty()) {
|
||||||
val issuer = "None"
|
if (title == null || title.isEmpty())"None" else title
|
||||||
val otpLabel = "$issuer:$accountName"
|
} else {
|
||||||
val otpSecret = otpElement.getBase32Secret()
|
otpElement.issuer
|
||||||
val otpDigits = otpElement.digits
|
}
|
||||||
val otpIssuer = otpElement.issuer
|
val accountName = if (username == null || username.isEmpty()) "OTP" else username
|
||||||
val otpAlgorithm = otpElement.algorithm.toString()
|
val uriString = StringBuilder("otpauth://$otpAuthority/$issuer:$accountName" +
|
||||||
|
"?$SECRET_URL_PARAM=${otpElement.getBase32Secret()}" +
|
||||||
val uriString = "otpauth://$otpAuthority/$otpLabel" +
|
|
||||||
"?$SECRET_URL_PARAM=$otpSecret" +
|
|
||||||
"&$counterOrPeriodLabel=$counterOrPeriodValue" +
|
"&$counterOrPeriodLabel=$counterOrPeriodValue" +
|
||||||
"&$DIGITS_URL_PARAM=$otpDigits" +
|
"&$DIGITS_URL_PARAM=${otpElement.digits}" +
|
||||||
"&$ISSUER_URL_PARAM=$otpIssuer" +
|
"&$ISSUER_URL_PARAM=$issuer")
|
||||||
"&$ALGORITHM_URL_PARAM=$otpAlgorithm" +
|
if (otpElement.tokenType == OtpTokenType.STEAM) {
|
||||||
"\n"
|
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.toString())
|
||||||
|
|
||||||
return Uri.parse(uriString)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseTOTPKeyValues(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
|
private fun parseTOTPKeyValues(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
|
||||||
@@ -296,8 +299,9 @@ object OtpEntryFields {
|
|||||||
/**
|
/**
|
||||||
* Build Otp field from an OtpElement
|
* Build Otp field from an OtpElement
|
||||||
*/
|
*/
|
||||||
fun buildOtpField(otpElement: OtpElement): Field {
|
fun buildOtpField(otpElement: OtpElement, title: String?, username: String?): Field {
|
||||||
return Field(OTP_FIELD, ProtectedString(true, buildOtpUri(otpElement).toString()))
|
return Field(OTP_FIELD, ProtectedString(true,
|
||||||
|
buildOtpUri(otpElement, title, username).toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -42,7 +42,16 @@ public class TokenCalculator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public enum HashAlgorithm {
|
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;
|
public static final HashAlgorithm DEFAULT_ALGORITHM = HashAlgorithm.SHA1;
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
|
|||||||
/**
|
/**
|
||||||
* Add a new view to fill in the information of the customized field
|
* 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, "")) {
|
fun addNewCustomField(name: String = "", value: ProtectedString = ProtectedString(false, "")) {
|
||||||
val entryEditCustomField = EntryEditCustomField(context).apply {
|
val entryEditCustomField = EntryEditCustomField(context).apply {
|
||||||
setData(name, value)
|
setData(name, value)
|
||||||
|
|||||||
@@ -88,6 +88,18 @@
|
|||||||
android:layout_margin="@dimen/default_margin"
|
android:layout_margin="@dimen/default_margin"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- Algorithm -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/otp_algorithm"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"/>
|
||||||
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
|
android:id="@+id/setup_otp_algorithm"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<!-- Period / Counter -->
|
<!-- Period / Counter -->
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/setup_otp_period_title"
|
android:id="@+id/setup_otp_period_title"
|
||||||
@@ -141,18 +153,6 @@
|
|||||||
tools:targetApi="jelly_bean" />
|
tools:targetApi="jelly_bean" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<!-- Algorithm -->
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/otp_algorithm"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"/>
|
|
||||||
<androidx.appcompat.widget.AppCompatSpinner
|
|
||||||
android:id="@+id/setup_otp_algorithm"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="48dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user