Parse new TOTP fields from KeePass 2.47 #850

This commit is contained in:
J-Jamet
2021-01-14 11:48:44 +01:00
parent 3e2271e596
commit dc40b50b65

View File

@@ -57,13 +57,25 @@ object OtpEntryFields {
private const val DIGITS_KEY = "size" private const val DIGITS_KEY = "size"
private const val STEP_KEY = "step" private const val STEP_KEY = "step"
// HmacOtp KeePass2 values (https://keepass.info/help/base/placeholders.html#hmacotp) // HmacOtp KeePass2 values (https://keepass.info/help/base/placeholders.html#otp)
private const val HMACOTP_SECRET_FIELD = "HmacOtp-Secret" private const val HMACOTP_SECRET_FIELD = "HmacOtp-Secret"
private const val HMACOTP_SECRET_HEX_FIELD = "HmacOtp-Secret-Hex" private const val HMACOTP_SECRET_HEX_FIELD = "HmacOtp-Secret-Hex"
private const val HMACOTP_SECRET_BASE32_FIELD = "HmacOtp-Secret-Base32" private const val HMACOTP_SECRET_BASE32_FIELD = "HmacOtp-Secret-Base32"
private const val HMACOTP_SECRET_BASE64_FIELD = "HmacOtp-Secret-Base64" private const val HMACOTP_SECRET_BASE64_FIELD = "HmacOtp-Secret-Base64"
private const val HMACOTP_SECRET_COUNTER_FIELD = "HmacOtp-Counter" private const val HMACOTP_SECRET_COUNTER_FIELD = "HmacOtp-Counter"
// TimeOtp KeePass2 values
private const val TIMEOTP_SECRET_FIELD = "TimeOtp-Secret"
private const val TIMEOTP_SECRET_HEX_FIELD = "TimeOtp-Secret-Hex"
private const val TIMEOTP_SECRET_BASE32_FIELD = "TimeOtp-Secret-Base32"
private const val TIMEOTP_SECRET_BASE64_FIELD = "TimeOtp-Secret-Base64"
private const val TIMEOTP_LENGTH_FIELD = "TimeOtp-Length"
private const val TIMEOTP_PERIOD_FIELD = "TimeOtp-Period"
private const val TIMEOTP_ALGORITHM_FIELD = "TimeOtp-Algorithm"
private const val TIMEOTP_ALGORITHM_SHA1_VALUE = "HMAC-SHA-1"
private const val TIMEOTP_ALGORITHM_SHA256_VALUE = "HMAC-SHA-256"
private const val TIMEOTP_ALGORITHM_SHA512_VALUE = "HMAC-SHA-512"
// Custom fields (maybe from plugin) // Custom fields (maybe from plugin)
private const val TOTP_SEED_FIELD = "TOTP Seed" private const val TOTP_SEED_FIELD = "TOTP Seed"
private const val TOTP_SETTING_FIELD = "TOTP Settings" private const val TOTP_SETTING_FIELD = "TOTP Settings"
@@ -85,14 +97,17 @@ object OtpEntryFields {
// OTP (HOTP/TOTP) from URL and field from KeePassXC // OTP (HOTP/TOTP) from URL and field from KeePassXC
if (parseOTPUri(getField, otpElement)) if (parseOTPUri(getField, otpElement))
return otpElement return otpElement
// TOTP from KeePass 2.47
if (parseTOTPFromOfficialField(getField, otpElement))
return otpElement
// TOTP from key values (maybe plugin or old KeePassXC) // TOTP from key values (maybe plugin or old KeePassXC)
if (parseTOTPKeyValues(getField, otpElement)) if (parseTOTPKeyValues(getField, otpElement))
return otpElement return otpElement
// TOTP from custom field // TOTP from custom field
if (parseTOTPFromField(getField, otpElement)) if (parseTOTPFromPluginField(getField, otpElement))
return otpElement return otpElement
// HOTP fields from KeePass 2 // HOTP fields from KeePass 2
if (parseHOTPFromField(getField, otpElement)) if (parseHOTPFromOfficialField(getField, otpElement))
return otpElement return otpElement
return null return null
} }
@@ -266,6 +281,39 @@ object OtpEntryFields {
return Uri.encode(parameter.removeLineChars()) return Uri.encode(parameter.removeLineChars())
} }
private fun parseTOTPFromOfficialField(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
val secretField = getField(TIMEOTP_SECRET_FIELD)
val secretHexField = getField(TIMEOTP_SECRET_HEX_FIELD)
val secretBase32Field = getField(TIMEOTP_SECRET_BASE32_FIELD)
val secretBase64Field = getField(TIMEOTP_SECRET_BASE64_FIELD)
val lengthField = getField(TIMEOTP_LENGTH_FIELD)
val periodField = getField(TIMEOTP_PERIOD_FIELD)
val algorithmField = getField(TIMEOTP_ALGORITHM_FIELD)
try {
when {
secretField != null -> otpElement.setUTF8Secret(secretField)
secretHexField != null -> otpElement.setHexSecret(secretHexField)
secretBase32Field != null -> otpElement.setBase32Secret(secretBase32Field)
secretBase64Field != null -> otpElement.setBase64Secret(secretBase64Field)
lengthField != null -> otpElement.digits = lengthField.toIntOrNull() ?: OTP_DEFAULT_DIGITS
periodField != null -> otpElement.period = periodField.toIntOrNull() ?: TOTP_DEFAULT_PERIOD
algorithmField != null -> otpElement.algorithm =
when (algorithmField.toUpperCase(Locale.ENGLISH)) {
TIMEOTP_ALGORITHM_SHA1_VALUE -> HashAlgorithm.SHA1
TIMEOTP_ALGORITHM_SHA256_VALUE -> HashAlgorithm.SHA256
TIMEOTP_ALGORITHM_SHA512_VALUE -> HashAlgorithm.SHA512
else -> HashAlgorithm.SHA1
}
else -> return false
}
} catch (exception: Exception) {
return false
}
otpElement.type = OtpType.TOTP
return true
}
private fun parseTOTPKeyValues(getField: (id: String) -> String?, otpElement: OtpElement): Boolean { private fun parseTOTPKeyValues(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
val plainText = getField(OTP_FIELD) val plainText = getField(OTP_FIELD)
if (plainText != null && plainText.isNotEmpty()) { if (plainText != null && plainText.isNotEmpty()) {
@@ -291,7 +339,7 @@ object OtpEntryFields {
return false return false
} }
private fun parseTOTPFromField(getField: (id: String) -> String?, otpElement: OtpElement): Boolean { private fun parseTOTPFromPluginField(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
val seedField = getField(TOTP_SEED_FIELD) ?: return false val seedField = getField(TOTP_SEED_FIELD) ?: return false
try { try {
otpElement.setBase32Secret(seedField) otpElement.setBase32Secret(seedField)
@@ -317,7 +365,7 @@ object OtpEntryFields {
return true return true
} }
private fun parseHOTPFromField(getField: (id: String) -> String?, otpElement: OtpElement): Boolean { private fun parseHOTPFromOfficialField(getField: (id: String) -> String?, otpElement: OtpElement): Boolean {
val secretField = getField(HMACOTP_SECRET_FIELD) val secretField = getField(HMACOTP_SECRET_FIELD)
val secretHexField = getField(HMACOTP_SECRET_HEX_FIELD) val secretHexField = getField(HMACOTP_SECRET_HEX_FIELD)
val secretBase32Field = getField(HMACOTP_SECRET_BASE32_FIELD) val secretBase32Field = getField(HMACOTP_SECRET_BASE32_FIELD)
@@ -383,25 +431,43 @@ object OtpEntryFields {
val totpSeedField = Field(TOTP_SEED_FIELD) val totpSeedField = Field(TOTP_SEED_FIELD)
val totpSettingField = Field(TOTP_SETTING_FIELD) val totpSettingField = Field(TOTP_SETTING_FIELD)
val hmacOtpSecretField = Field(HMACOTP_SECRET_FIELD) val hmacOtpSecretField = Field(HMACOTP_SECRET_FIELD)
val hmacOtpSecretHewField = Field(HMACOTP_SECRET_HEX_FIELD) val hmacOtpSecretHexField = Field(HMACOTP_SECRET_HEX_FIELD)
val hmacOtpSecretBase32Field = Field(HMACOTP_SECRET_BASE32_FIELD) val hmacOtpSecretBase32Field = Field(HMACOTP_SECRET_BASE32_FIELD)
val hmacOtpSecretBase64Field = Field(HMACOTP_SECRET_BASE64_FIELD) val hmacOtpSecretBase64Field = Field(HMACOTP_SECRET_BASE64_FIELD)
val hmacOtpSecretCounterField = Field(HMACOTP_SECRET_COUNTER_FIELD) val hmacOtpSecretCounterField = Field(HMACOTP_SECRET_COUNTER_FIELD)
val timeOtpSecretField = Field(TIMEOTP_SECRET_FIELD)
val timeOtpSecretHexField = Field(TIMEOTP_SECRET_HEX_FIELD)
val timeOtpSecretBase32Field = Field(TIMEOTP_SECRET_BASE32_FIELD)
val timeOtpSecretBase64Field = Field(TIMEOTP_SECRET_BASE64_FIELD)
val timeOtpLengthField = Field(TIMEOTP_LENGTH_FIELD)
val timeOtpPeriodField = Field(TIMEOTP_PERIOD_FIELD)
val timeOtpAlgorithmField = Field(TIMEOTP_ALGORITHM_FIELD)
newCustomFields.remove(otpField) newCustomFields.remove(otpField)
newCustomFields.remove(totpSeedField) newCustomFields.remove(totpSeedField)
newCustomFields.remove(totpSettingField) newCustomFields.remove(totpSettingField)
newCustomFields.remove(hmacOtpSecretField) newCustomFields.remove(hmacOtpSecretField)
newCustomFields.remove(hmacOtpSecretHewField) newCustomFields.remove(hmacOtpSecretHexField)
newCustomFields.remove(hmacOtpSecretBase32Field) newCustomFields.remove(hmacOtpSecretBase32Field)
newCustomFields.remove(hmacOtpSecretBase64Field) newCustomFields.remove(hmacOtpSecretBase64Field)
newCustomFields.remove(hmacOtpSecretCounterField) newCustomFields.remove(hmacOtpSecretCounterField)
newCustomFields.remove(timeOtpSecretField)
newCustomFields.remove(timeOtpSecretHexField)
newCustomFields.remove(timeOtpSecretBase32Field)
newCustomFields.remove(timeOtpSecretBase64Field)
newCustomFields.remove(timeOtpLengthField)
newCustomFields.remove(timeOtpPeriodField)
newCustomFields.remove(timeOtpAlgorithmField)
// Empty auto generated OTP Token field // Empty auto generated OTP Token field
if (fieldsToParse.contains(otpField) if (fieldsToParse.contains(otpField)
|| fieldsToParse.contains(totpSeedField) || fieldsToParse.contains(totpSeedField)
|| fieldsToParse.contains(hmacOtpSecretField) || fieldsToParse.contains(hmacOtpSecretField)
|| fieldsToParse.contains(hmacOtpSecretHewField) || fieldsToParse.contains(hmacOtpSecretHexField)
|| fieldsToParse.contains(hmacOtpSecretBase32Field) || fieldsToParse.contains(hmacOtpSecretBase32Field)
|| fieldsToParse.contains(hmacOtpSecretBase64Field) || fieldsToParse.contains(hmacOtpSecretBase64Field)
|| fieldsToParse.contains(timeOtpSecretField)
|| fieldsToParse.contains(timeOtpSecretHexField)
|| fieldsToParse.contains(timeOtpSecretBase32Field)
|| fieldsToParse.contains(timeOtpSecretBase64Field)
) )
newCustomFields.add(Field(OTP_TOKEN_FIELD)) newCustomFields.add(Field(OTP_TOKEN_FIELD))
return newCustomFields return newCustomFields