Skip to content

Commit

Permalink
Add backup codes to OTP setup
Browse files Browse the repository at this point in the history
  • Loading branch information
Rookiestyle committed Oct 18, 2021
1 parent 4a33c8e commit 606e036
Show file tree
Hide file tree
Showing 11 changed files with 522 additions and 273 deletions.
6 changes: 3 additions & 3 deletions Translations/KeePassOTP.de.language.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Increment the TranslationVersion every time the translation file is updated
Also update the version.info file
-->
<TranslationVersion>20</TranslationVersion>
<TranslationVersion>21</TranslationVersion>
<item>
<key>OTPCopyTrayNoEntries</key>
<value>KPOTP - Keine Einträge vorhanden</value>
Expand Down Expand Up @@ -293,7 +293,7 @@ Tatsächliche Anzahl Datensätze: {0}</value>
</item>
<item>
<key>ReadScreenForQRCode</key>
<value>Vom Bildschrim lesen</value>
<value>Lese QR Code vom Bildschirm</value>
</item>
<item>
<key>ReadScreenForQRCodeExplain</key>
Expand All @@ -304,7 +304,7 @@ Dieses Hinweis wird nicht erneut angezeigt.</value>
</item>
<item>
<key>OTP_Setup_DragDrop</key>
<value>Ziehe einen gültigen OTP QR code hierhin</value>
<value>Ziehe einen gültigen OTP QR Code hierhin</value>
</item>
<item>
<key>OTPRenewal</key>
Expand Down
4 changes: 4 additions & 0 deletions Translations/KeePassOTP.template.language.xml
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,8 @@ This message will not be shown again.</value>
<key>OTPDisplayMode_label</key>
<value>OTP display mode:</value>
</item>
<item>
<key>RecoveryCodes</key>
<value>Recovery Codes</value>
</item>
</Translation>
32 changes: 32 additions & 0 deletions src/DAO/OTPDAO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,38 @@ public void InitIssuerLabel(KPOTP otp, PwEntry pe)
if (string.IsNullOrEmpty(otp.Issuer)) otp.Issuer = PluginTranslation.PluginTranslate.PluginName;
otp.Label = GetDereferencedString(pe, pe.Strings.ReadSafe(PwDefs.UserNameField));
}

public static ProtectedString ConvertToString(List<ProtectedString> lCodes)
{
ProtectedString psResult = ProtectedString.EmptyEx;
for (int i = 0; i < lCodes.Count; i++)
{
if (i > 0) psResult += new ProtectedString(true, new byte[] { (byte)'\n' });
psResult += lCodes[i];
}
return psResult;
}

protected static List<ProtectedString> ConvertFromString(ProtectedString psCodes)
{
List<ProtectedString> lCodes = new List<ProtectedString>();
if (psCodes == null || psCodes.IsEmpty) return lCodes;
var cChars = psCodes.ReadChars();

ProtectedString ps = ProtectedString.EmptyEx;
foreach (var c in cChars)
{
if (c == '\n' || c == '\r')
{
if (!ps.IsEmpty) lCodes.Add(ps);
ps = ProtectedString.EmptyEx;
}
else ps += new ProtectedString(true, new byte[] { (byte)c });
}
if (!ps.IsEmpty) lCodes.Add(ps);
MemUtil.ZeroArray(cChars);
return lCodes;
}
}
}
}
12 changes: 11 additions & 1 deletion src/DAO/OTPDAO_DB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,11 @@ public override void SaveOTP(KPOTP myOTP, PwEntry pe)
m_peOTP.CustomData.Remove(Config.TIMECORRECTION);
else
m_peOTP.CustomData.Set(Config.TIMECORRECTION, myOTP.TimeCorrectionUrl);

if (myOTP.RecoveryCodes == null || myOTP.RecoveryCodes.IsEmpty)
m_peOTP.Strings.Remove(Config.RECOVERY);
else
m_peOTP.Strings.Set(Config.RECOVERY, myOTP.RecoveryCodes);

Touch(m_peOTP);
if (myOTP.OTPSeed.IsEmpty)
m_pe.CustomData.Remove(DBNAME);
Expand Down Expand Up @@ -678,6 +682,7 @@ private KPOTP GetSettings()
catch { return EmptyKPOTDB; }

myOTP.OTPAuthString = dStrings.GetSafe(Config.OTPFIELD);
myOTP.RecoveryCodes = dStrings.GetSafe(Config.RECOVERY);
string timeCorrection = dCustomData.Get(Config.TIMECORRECTION);
timeCorrection = string.IsNullOrEmpty(timeCorrection) ? string.Empty : timeCorrection;
if (timeCorrection == "OWNURL")
Expand Down Expand Up @@ -941,16 +946,20 @@ public int MigrateOTP2DB()
{
m_pe = pe;
ProtectedString otpfield = m_pe.Strings.GetSafe(Config.OTPFIELD);
ProtectedString recovery = m_pe.Strings.GetSafe(Config.RECOVERY);
string timecorrection = m_pe.CustomData.Get(Config.TIMECORRECTION);
if (otpfield.IsEmpty) continue;
bool bCreated = false;
GetOTPEntry(true, out bCreated);
m_peOTP.Strings.Set(Config.OTPFIELD, otpfield);
if (!recovery.IsEmpty) m_peOTP.Strings.Set(Config.RECOVERY, recovery);
else m_peOTP.Strings.Remove(Config.RECOVERY);
if (!string.IsNullOrEmpty(timecorrection)) m_peOTP.CustomData.Set(Config.TIMECORRECTION, timecorrection);
else m_peOTP.CustomData.Remove(Config.TIMECORRECTION);
//Seed has been added to OTP db, increase moved-counter
moved++;
m_pe.Strings.Remove(Config.OTPFIELD);
m_pe.Strings.Remove(Config.RECOVERY);
m_pe.CustomData.Remove(Config.TIMECORRECTION);
m_pe.CustomData.Set(DBNAME, StrUtil.BoolToString(true));
m_pe.Touch(true, false);
Expand All @@ -973,6 +982,7 @@ public int MigrateOTP2Entry()
foreach (PwEntry pe in DB.RootGroup.GetEntries(true).Where(x => uuid.Equals(x.Uuid)))
{
pe.Strings.Set(Config.OTPFIELD, peOTP.Strings.GetSafe(Config.OTPFIELD));
if (peOTP.Strings.Exists(Config.RECOVERY)) pe.Strings.Set(Config.RECOVERY, peOTP.Strings.GetSafe(Config.RECOVERY));
if (peOTP.CustomData.Exists(Config.TIMECORRECTION))
pe.CustomData.Set(Config.TIMECORRECTION, peOTP.CustomData.Get(Config.TIMECORRECTION));
else
Expand Down
12 changes: 9 additions & 3 deletions src/DAO/OTPDAO_Entry.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using KeePass;
using KeePassLib;
using KeePassLib.Security;
Expand Down Expand Up @@ -70,12 +71,15 @@ public override void SaveOTP(KPOTP myOTP, PwEntry pe)
{
pe.Strings.Remove(Config.OTPFIELD);
pe.CustomData.Remove(Config.TIMECORRECTION);
pe.Strings.Remove(Config.RECOVERY);
}
else
{
//pe.Strings.Set(Config.SETTINGS, new ProtectedString(false, otpSettings));
//pe.Strings.Set(Config.SEED, myOTP.OTPSeed);
pe.Strings.Set(Config.OTPFIELD, myOTP.OTPAuthString);
if (myOTP.RecoveryCodes.IsEmpty) pe.Strings.Remove(Config.RECOVERY);
else pe.Strings.Set(Config.RECOVERY, myOTP.RecoveryCodes);
if (myOTP.TimeCorrectionUrlOwn)
pe.CustomData.Set(Config.TIMECORRECTION, "OWNURL");
else if (string.IsNullOrEmpty(myOTP.TimeCorrectionUrl) || (myOTP.TimeCorrectionUrl == "OFF"))
Expand All @@ -86,10 +90,11 @@ public override void SaveOTP(KPOTP myOTP, PwEntry pe)
pe.Touch(true, false);
}

private EntryOTP EnsureEntry(PwEntry pe)
private EntryOTP EnsureEntry(PwEntry pe)
{
EntryOTP otp;
if (m_dEntryOTPData.TryGetValue(pe, out otp) && !IgnoreBuffer) return otp;
if (m_dEntryOTPData.TryGetValue(pe, out otp) && !IgnoreBuffer)
return otp;
otp.db = pe.GetDB();
otp.Loaded = true;
otp.OTPDefined = OTPDefined(pe);
Expand All @@ -113,7 +118,7 @@ private EntryOTP EnsureEntry(PwEntry pe)
return otp;
}

public override OTPDefinition OTPDefined(PwEntry pe)
public override OTPDefinition OTPDefined(PwEntry pe)
{
return pe.Strings.Exists(Config.OTPFIELD) ? OTPDefinition.Complete : OTPDefinition.None;
}
Expand All @@ -130,6 +135,7 @@ private static KPOTP GetSettings(PwEntry pe)
myOTP.OTPSeed = pe.Strings.GetSafe(Config.SEED);
*/
myOTP.OTPAuthString = pe.Strings.Get(Config.OTPFIELD);
myOTP.RecoveryCodes = pe.Strings.GetSafe(Config.RECOVERY);
string timeCorrection = pe.CustomData.Get(Config.TIMECORRECTION);
timeCorrection = string.IsNullOrEmpty(timeCorrection) ? string.Empty : timeCorrection;
if (timeCorrection == "OWNURL")
Expand Down
11 changes: 10 additions & 1 deletion src/KeePassOTP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ public int RemainingSeconds
}
}

public ProtectedString RecoveryCodes = ProtectedString.EmptyEx;

static KPOTP()
{
miConfigureWebClient = typeof(IOConnection).GetMethod("ConfigureWebClient",
Expand All @@ -157,6 +159,11 @@ static KPOTP()
null);
}

public KPOTP()
{

}

public string GetOTP()
{
return GetOTP(false, false);
Expand Down Expand Up @@ -693,6 +700,7 @@ public static bool Equals(KPOTP otp1, KPOTP otp2, string url, out bool OnlyCount
if ((otp1.TimeCorrectionUrl == "OWNURL") && !string.IsNullOrEmpty(url) && (otp2.TimeCorrectionUrl != url)) return false;
if ((otp2.TimeCorrectionUrl == "OWNURL") && !string.IsNullOrEmpty(url) && (otp1.TimeCorrectionUrl != url)) return false;
}
if (!otp1.RecoveryCodes.Equals(otp2.RecoveryCodes, false)) return false;

if (otp1.Type == KPOTPType.HOTP && (otp1.HOTPCounter != otp2.HOTPCounter))
{
Expand All @@ -702,7 +710,7 @@ public static bool Equals(KPOTP otp1, KPOTP otp2, string url, out bool OnlyCount
return true;
}

public KPOTP Clone()
public KPOTP Clone()
{
KPOTP result = new KPOTP();

Expand All @@ -718,6 +726,7 @@ public KPOTP Clone()
result.TimeCorrectionUrlOwn = this.TimeCorrectionUrlOwn;
result.TOTPTimestep = this.TOTPTimestep;
result.OTPTimeCorrection = this.OTPTimeCorrection;
result.RecoveryCodes = new ProtectedString(true, this.RecoveryCodes.ReadUtf8());

return result;
}
Expand Down
Loading

0 comments on commit 606e036

Please sign in to comment.