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 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_HEX_FIELD = "HmacOtp-Secret-Hex"
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_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)
private const val TOTP_SEED_FIELD = "TOTP Seed"
private const val TOTP_SETTING_FIELD = "TOTP Settings"
@@ -85,14 +97,17 @@ object OtpEntryFields {
// OTP (HOTP/TOTP) from URL and field from KeePassXC
if (parseOTPUri(getField, otpElement))
return otpElement
// TOTP from KeePass 2.47
if (parseTOTPFromOfficialField(getField, otpElement))
return otpElement
// TOTP from key values (maybe plugin or old KeePassXC)
if (parseTOTPKeyValues(getField, otpElement))
return otpElement
// TOTP from custom field
if (parseTOTPFromField(getField, otpElement))
if (parseTOTPFromPluginField(getField, otpElement))
return otpElement
// HOTP fields from KeePass 2
if (parseHOTPFromField(getField, otpElement))
if (parseHOTPFromOfficialField(getField, otpElement))
return otpElement
return null
}
@@ -266,6 +281,39 @@ object OtpEntryFields {
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 {
val plainText = getField(OTP_FIELD)
if (plainText != null && plainText.isNotEmpty()) {
@@ -291,7 +339,7 @@ object OtpEntryFields {
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
try {
otpElement.setBase32Secret(seedField)
@@ -317,7 +365,7 @@ object OtpEntryFields {
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 secretHexField = getField(HMACOTP_SECRET_HEX_FIELD)
val secretBase32Field = getField(HMACOTP_SECRET_BASE32_FIELD)
@@ -383,25 +431,43 @@ object OtpEntryFields {
val totpSeedField = Field(TOTP_SEED_FIELD)
val totpSettingField = Field(TOTP_SETTING_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 hmacOtpSecretBase64Field = Field(HMACOTP_SECRET_BASE64_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(totpSeedField)
newCustomFields.remove(totpSettingField)
newCustomFields.remove(hmacOtpSecretField)
newCustomFields.remove(hmacOtpSecretHewField)
newCustomFields.remove(hmacOtpSecretHexField)
newCustomFields.remove(hmacOtpSecretBase32Field)
newCustomFields.remove(hmacOtpSecretBase64Field)
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
if (fieldsToParse.contains(otpField)
|| fieldsToParse.contains(totpSeedField)
|| fieldsToParse.contains(hmacOtpSecretField)
|| fieldsToParse.contains(hmacOtpSecretHewField)
|| fieldsToParse.contains(hmacOtpSecretHexField)
|| fieldsToParse.contains(hmacOtpSecretBase32Field)
|| fieldsToParse.contains(hmacOtpSecretBase64Field)
|| fieldsToParse.contains(timeOtpSecretField)
|| fieldsToParse.contains(timeOtpSecretHexField)
|| fieldsToParse.contains(timeOtpSecretBase32Field)
|| fieldsToParse.contains(timeOtpSecretBase64Field)
)
newCustomFields.add(Field(OTP_TOKEN_FIELD))
return newCustomFields