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_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
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -88,6 +88,18 @@
|
||||
android:layout_margin="@dimen/default_margin"
|
||||
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 -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/setup_otp_period_title"
|
||||
@@ -141,18 +153,6 @@
|
||||
tools:targetApi="jelly_bean" />
|
||||
</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>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
||||
|
||||
Reference in New Issue
Block a user