mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-08-13 17:03:49 -04:00
fixed problems with OTP, completed implementation to work with cloud storage
This commit is contained in:
parent
aeaba47573
commit
3fde2c9846
@ -20,7 +20,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>False</Optimize>
|
<Optimize>False</Optimize>
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
|
@ -275,6 +275,27 @@ namespace keepass2android.Io
|
|||||||
return _jfs.CreateFilePath(parent, newFilename);
|
return _jfs.CreateFilePath(parent, newFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return IoUtil.GetParentPath(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return IOConnectionInfo.FromPath(
|
||||||
|
ListContents(folderPath).Where(desc => { return desc.DisplayName == filename; })
|
||||||
|
.Single()
|
||||||
|
.Path);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception("Error finding " + filename + " in " + folderPath.GetDisplayName(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private DateTime JavaTimeToCSharp(long javatime)
|
private DateTime JavaTimeToCSharp(long javatime)
|
||||||
{
|
{
|
||||||
return new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
return new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -45,7 +45,7 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Run ()
|
public override void Run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -368,56 +368,68 @@ namespace keepass2android
|
|||||||
return base.OnOptionsItemSelected(item);
|
return base.OnOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SyncOtpAuxFile: OnFinish
|
public class SyncOtpAuxFile: RunnableOnFinish
|
||||||
{
|
{
|
||||||
private readonly IOConnectionInfo _ioc;
|
private readonly IOConnectionInfo _ioc;
|
||||||
|
|
||||||
public SyncOtpAuxFile(IOConnectionInfo ioc)
|
public SyncOtpAuxFile(IOConnectionInfo ioc) : base(null)
|
||||||
{
|
{
|
||||||
_ioc = ioc;
|
_ioc = ioc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Run()
|
public override void Run()
|
||||||
{
|
|
||||||
if (Handler != null)
|
|
||||||
{
|
|
||||||
Handler.Post(DoSyncOtpAuxFile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
DoSyncOtpAuxFile();
|
|
||||||
base.Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DoSyncOtpAuxFile()
|
|
||||||
{
|
{
|
||||||
StatusLogger.UpdateMessage(UiStringKey.SynchronizingOtpAuxFile);
|
StatusLogger.UpdateMessage(UiStringKey.SynchronizingOtpAuxFile);
|
||||||
|
try
|
||||||
|
{
|
||||||
//simply open the file. The file storage does a complete sync.
|
//simply open the file. The file storage does a complete sync.
|
||||||
using (App.Kp2a.GetOtpAuxFileStorage(_ioc).OpenFileForRead(_ioc))
|
using (App.Kp2a.GetOtpAuxFileStorage(_ioc).OpenFileForRead(_ioc))
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Finish(true);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
|
||||||
|
Finish(false, e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Synchronize()
|
private void Synchronize()
|
||||||
{
|
{
|
||||||
var filestorage = App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc);
|
var filestorage = App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc);
|
||||||
RunnableOnFinish task;
|
RunnableOnFinish task;
|
||||||
ActionOnFinish onFinishShowMessage = new ActionOnFinish((success, message) =>
|
OnFinish onFinish = new ActionOnFinish((success, message) =>
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrEmpty(message))
|
if (!String.IsNullOrEmpty(message))
|
||||||
Toast.MakeText(this, message, ToastLength.Long).Show();
|
Toast.MakeText(this, message, ToastLength.Long).Show();
|
||||||
|
|
||||||
|
if (App.Kp2a.GetDb().OtpAuxFileIoc != null)
|
||||||
|
{
|
||||||
|
var task2 = new SyncOtpAuxFile(App.Kp2a.GetDb().OtpAuxFileIoc);
|
||||||
|
new ProgressTask(App.Kp2a, this, task2).Run();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (filestorage is CachingFileStorage)
|
if (filestorage is CachingFileStorage)
|
||||||
{
|
{
|
||||||
|
|
||||||
task = new SynchronizeCachedDatabase(this, App.Kp2a, onFinishShowMessage);
|
task = new SynchronizeCachedDatabase(this, App.Kp2a, onFinish);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
task = new CheckDatabaseForChanges(this, App.Kp2a, onFinishShowMessage);
|
task = new CheckDatabaseForChanges(this, App.Kp2a, onFinish);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var progressTask = new ProgressTask(App.Kp2a, this, task);
|
var progressTask = new ProgressTask(App.Kp2a, this, task);
|
||||||
progressTask.Run();
|
progressTask.Run();
|
||||||
|
|
||||||
|
@ -268,8 +268,16 @@ namespace keepass2android
|
|||||||
new LoadingDialog<object, object, object>(this, true,
|
new LoadingDialog<object, object, object>(this, true,
|
||||||
//doInBackground
|
//doInBackground
|
||||||
delegate
|
delegate
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_otpInfo = OathHotpKeyProv.LoadOtpInfo(new KeyProviderQueryContext(_ioConnection, false, false));
|
_otpInfo = OathHotpKeyProv.LoadOtpInfo(new KeyProviderQueryContext(_ioConnection, false, false));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
//onPostExecute
|
//onPostExecute
|
||||||
@ -694,8 +702,8 @@ namespace keepass2android
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var lOtps = GetOtpsFromUI();
|
var lOtps = GetOtpsFromUi();
|
||||||
CreateOtpSecret(lOtps);
|
OathHotpKeyProv.CreateOtpSecret(lOtps, _otpInfo);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@ -731,7 +739,12 @@ namespace keepass2android
|
|||||||
MakePasswordMaskedOrVisible();
|
MakePasswordMaskedOrVisible();
|
||||||
|
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, compositeKey, _keyFileOrProvider, new AfterLoad(handler, this));
|
OnFinish onFinish = new AfterLoad(handler, this);
|
||||||
|
|
||||||
|
LoadDb task = (KeyProviderType == KeyProviders.Otp) ?
|
||||||
|
new SaveOtpAuxFileAndLoadDb(App.Kp2a, _ioConnection, _loadDbTask, compositeKey, _keyFileOrProvider, onFinish, this)
|
||||||
|
:
|
||||||
|
new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, compositeKey, _keyFileOrProvider, onFinish);
|
||||||
_loadDbTask = null; // prevent accidental re-use
|
_loadDbTask = null; // prevent accidental re-use
|
||||||
|
|
||||||
SetNewDefaultFile();
|
SetNewDefaultFile();
|
||||||
@ -739,7 +752,7 @@ namespace keepass2android
|
|||||||
new ProgressTask(App.Kp2a, this, task).Run();
|
new ProgressTask(App.Kp2a, this, task).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetOtpsFromUI()
|
private List<string> GetOtpsFromUi()
|
||||||
{
|
{
|
||||||
List<string> lOtps = new List<string>();
|
List<string> lOtps = new List<string>();
|
||||||
foreach (int otpId in _otpTextViewIds)
|
foreach (int otpId in _otpTextViewIds)
|
||||||
@ -751,43 +764,6 @@ namespace keepass2android
|
|||||||
return lOtps;
|
return lOtps;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateOtpSecret(List<string> lOtps)
|
|
||||||
{
|
|
||||||
byte[] pbSecret;
|
|
||||||
if (!string.IsNullOrEmpty(_otpInfo.EncryptedSecret)) // < v2.0
|
|
||||||
{
|
|
||||||
byte[] pbKey32 = OtpUtil.KeyFromOtps(lOtps.ToArray(), 0,
|
|
||||||
lOtps.Count, Convert.FromBase64String(
|
|
||||||
_otpInfo.TransformationKey), _otpInfo.TransformationRounds);
|
|
||||||
if (pbKey32 == null) throw new InvalidOperationException();
|
|
||||||
|
|
||||||
pbSecret = OtpUtil.DecryptData(_otpInfo.EncryptedSecret,
|
|
||||||
pbKey32, Convert.FromBase64String(_otpInfo.EncryptionIV));
|
|
||||||
if (pbSecret == null) throw new InvalidOperationException();
|
|
||||||
|
|
||||||
_otpInfo.Secret = pbSecret;
|
|
||||||
_otpInfo.Counter += (ulong) _otpInfo.OtpsRequired;
|
|
||||||
}
|
|
||||||
else // >= v2.0, supporting look-ahead
|
|
||||||
{
|
|
||||||
bool bSuccess = false;
|
|
||||||
for (int i = 0; i < _otpInfo.EncryptedSecrets.Count; ++i)
|
|
||||||
{
|
|
||||||
OtpEncryptedData d = _otpInfo.EncryptedSecrets[i];
|
|
||||||
pbSecret = OtpUtil.DecryptSecret(d, lOtps.ToArray(), 0,
|
|
||||||
lOtps.Count);
|
|
||||||
if (pbSecret != null)
|
|
||||||
{
|
|
||||||
_otpInfo.Secret = pbSecret;
|
|
||||||
_otpInfo.Counter += ((ulong) _otpInfo.OtpsRequired +
|
|
||||||
(ulong) i);
|
|
||||||
bSuccess = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!bSuccess) throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MakePasswordMaskedOrVisible()
|
private void MakePasswordMaskedOrVisible()
|
||||||
{
|
{
|
||||||
@ -900,7 +876,7 @@ namespace keepass2android
|
|||||||
outState.PutStringArrayList(PendingOtpsKey, _pendingOtps);
|
outState.PutStringArrayList(PendingOtpsKey, _pendingOtps);
|
||||||
if (_otpInfo != null)
|
if (_otpInfo != null)
|
||||||
{
|
{
|
||||||
outState.PutStringArrayList(EnteredOtpsKey, GetOtpsFromUI());
|
outState.PutStringArrayList(EnteredOtpsKey, GetOtpsFromUi());
|
||||||
|
|
||||||
var sw = new StringWriter();
|
var sw = new StringWriter();
|
||||||
|
|
||||||
@ -1119,26 +1095,8 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Run() {
|
public override void Run()
|
||||||
|
|
||||||
if (_act.KeyProviderType == KeyProviders.Otp)
|
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
StatusLogger.UpdateMessage(UiStringKey.SavingOtpAuxFile);
|
|
||||||
|
|
||||||
if (!OathHotpKeyProv.CreateAuxFile(_act._otpInfo, new KeyProviderQueryContext(_act._ioConnection, false, false)))
|
|
||||||
Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile), ToastLength.Long).Show();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Kp2aLog.Log(e.Message);
|
|
||||||
|
|
||||||
Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile)+" "+e.Message, ToastLength.Long).Show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ( Success )
|
if ( Success )
|
||||||
{
|
{
|
||||||
_act.SetEditText(Resource.Id.password, "");
|
_act.SetEditText(Resource.Id.password, "");
|
||||||
@ -1150,8 +1108,6 @@ namespace keepass2android
|
|||||||
|
|
||||||
_act.LaunchNextActivity();
|
_act.LaunchNextActivity();
|
||||||
|
|
||||||
if ((_act.KeyProviderType == KeyProviders.Otp) || (_act.KeyProviderType == KeyProviders.OtpRecovery))
|
|
||||||
App.Kp2a.GetDb().OtpAuxFileIoc = OathHotpKeyProv.GetAuxFileIoc(_act._ioConnection);
|
|
||||||
|
|
||||||
GC.Collect(); // Ensure temporary memory used while loading is collected
|
GC.Collect(); // Ensure temporary memory used while loading is collected
|
||||||
}
|
}
|
||||||
@ -1162,7 +1118,45 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SaveOtpAuxFileAndLoadDb : LoadDb
|
||||||
|
{
|
||||||
|
private readonly PasswordActivity _act;
|
||||||
|
|
||||||
|
|
||||||
|
public SaveOtpAuxFileAndLoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, string keyfileOrProvider, OnFinish finish, PasswordActivity act) : base(app, ioc, databaseData, compositeKey, keyfileOrProvider, finish)
|
||||||
|
{
|
||||||
|
_act = act;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatusLogger.UpdateMessage(UiStringKey.SavingOtpAuxFile);
|
||||||
|
|
||||||
|
KeyProviderQueryContext ctx = new KeyProviderQueryContext(_act._ioConnection, false, false);
|
||||||
|
IOConnectionInfo auxFileIoc = OathHotpKeyProv.GetAuxFileIoc(_act._ioConnection);
|
||||||
|
if (!OathHotpKeyProv.CreateAuxFile(_act._otpInfo, ctx, auxFileIoc))
|
||||||
|
Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile), ToastLength.Long).Show();
|
||||||
|
|
||||||
|
App.Kp2a.GetDb().OtpAuxFileIoc = auxFileIoc;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.Message);
|
||||||
|
|
||||||
|
Toast.MakeText(_act, _act.GetString(Resource.String.ErrorUpdatingOtpAuxFile) + " " + e.Message,
|
||||||
|
ToastLength.Long).Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
base.Run();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6593
src/keepass2android/Resources/Resource.designer.cs
generated
6593
src/keepass2android/Resources/Resource.designer.cs
generated
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,7 @@ android:layout_height="match_parent"
|
|||||||
android:id="@+id/password_label"
|
android:id="@+id/password_label"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dip"
|
||||||
android:text="@string/master_key_type" />
|
android:text="@string/master_key_type" />
|
||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/password_mode_spinner"
|
android:id="@+id/password_mode_spinner"
|
||||||
@ -68,6 +69,7 @@ android:layout_height="match_parent"
|
|||||||
android:id="@+id/passwordLine"
|
android:id="@+id/passwordLine"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="false"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/password"
|
android:id="@+id/password"
|
||||||
@ -76,6 +78,7 @@ android:layout_height="match_parent"
|
|||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
android:hint="@string/hint_login_pass" />
|
android:hint="@string/hint_login_pass" />
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/toggle_password"
|
android:id="@+id/toggle_password"
|
||||||
@ -87,6 +90,7 @@ android:layout_height="match_parent"
|
|||||||
android:id="@+id/keyfileLine"
|
android:id="@+id/keyfileLine"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="false"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/pass_keyfile"
|
android:id="@+id/pass_keyfile"
|
||||||
@ -94,6 +98,7 @@ android:layout_height="match_parent"
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
android:hint="@string/entry_keyfile" />
|
android:hint="@string/entry_keyfile" />
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/browse_button"
|
android:id="@+id/browse_button"
|
||||||
|
@ -371,10 +371,13 @@
|
|||||||
<string name="ChangeLog_title">Change log</string>
|
<string name="ChangeLog_title">Change log</string>
|
||||||
|
|
||||||
<string name="ChangeLog_0_9_2">
|
<string name="ChangeLog_0_9_2">
|
||||||
<b>Version 0.9.2a (preview)</b>\n
|
<b>Version 0.9.2b (preview)</b>\n
|
||||||
|
* Added OTP support (compatible with OtpKeyProv plugin)\n
|
||||||
|
* Integrated NFC support for OTPs from YubiKey NEO \n
|
||||||
* Several UI improvements\n
|
* Several UI improvements\n
|
||||||
* Integrated Keepass 2.24 library\n
|
* Integrated Keepass 2.24 library\n
|
||||||
* Added option to kill the app process (see settings)\n
|
* Added option to kill the app process (see settings)\n
|
||||||
|
* Bug fixes\n
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="ChangeLog_0_9_1">
|
<string name="ChangeLog_0_9_1">
|
||||||
|
@ -137,7 +137,7 @@ namespace OtpKeyProv
|
|||||||
|
|
||||||
|
|
||||||
public static bool CreateAuxFile(OtpInfo otpInfo,
|
public static bool CreateAuxFile(OtpInfo otpInfo,
|
||||||
KeyProviderQueryContext ctx)
|
KeyProviderQueryContext ctx, IOConnectionInfo auxFileIoc)
|
||||||
{
|
{
|
||||||
otpInfo.Type = ProvType;
|
otpInfo.Type = ProvType;
|
||||||
otpInfo.Version = ProvVersion;
|
otpInfo.Version = ProvVersion;
|
||||||
@ -145,15 +145,54 @@ namespace OtpKeyProv
|
|||||||
|
|
||||||
otpInfo.EncryptSecret();
|
otpInfo.EncryptSecret();
|
||||||
|
|
||||||
IOConnectionInfo ioc = GetAuxFileIoc(ctx);
|
if(!OtpInfo.Save(auxFileIoc, otpInfo))
|
||||||
if(!OtpInfo.Save(ioc, otpInfo))
|
|
||||||
{
|
{
|
||||||
MessageService.ShowWarning("Failed to save auxiliary OTP info file:",
|
MessageService.ShowWarning("Failed to save auxiliary OTP info file:",
|
||||||
ioc.GetDisplayName());
|
auxFileIoc.GetDisplayName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void CreateOtpSecret(List<string> lOtps, OtpInfo otpInfo)
|
||||||
|
{
|
||||||
|
|
||||||
|
byte[] pbSecret;
|
||||||
|
if (!string.IsNullOrEmpty(otpInfo.EncryptedSecret)) // < v2.0
|
||||||
|
{
|
||||||
|
byte[] pbKey32 = OtpUtil.KeyFromOtps(lOtps.ToArray(), 0,
|
||||||
|
lOtps.Count, Convert.FromBase64String(
|
||||||
|
otpInfo.TransformationKey), otpInfo.TransformationRounds);
|
||||||
|
if (pbKey32 == null) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
pbSecret = OtpUtil.DecryptData(otpInfo.EncryptedSecret,
|
||||||
|
pbKey32, Convert.FromBase64String(otpInfo.EncryptionIV));
|
||||||
|
if (pbSecret == null) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
otpInfo.Secret = pbSecret;
|
||||||
|
otpInfo.Counter += otpInfo.OtpsRequired;
|
||||||
|
}
|
||||||
|
else // >= v2.0, supporting look-ahead
|
||||||
|
{
|
||||||
|
bool bSuccess = false;
|
||||||
|
for (int i = 0; i < otpInfo.EncryptedSecrets.Count; ++i)
|
||||||
|
{
|
||||||
|
OtpEncryptedData d = otpInfo.EncryptedSecrets[i];
|
||||||
|
pbSecret = OtpUtil.DecryptSecret(d, lOtps.ToArray(), 0,
|
||||||
|
lOtps.Count);
|
||||||
|
if (pbSecret != null)
|
||||||
|
{
|
||||||
|
otpInfo.Secret = pbSecret;
|
||||||
|
otpInfo.Counter += ((ulong) otpInfo.OtpsRequired +
|
||||||
|
(ulong) i);
|
||||||
|
bSuccess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bSuccess) throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,14 @@ namespace keepass2android.addons.OtpKeyProv
|
|||||||
{
|
{
|
||||||
OtpInfo remoteOtpInfo, localOtpInfo;
|
OtpInfo remoteOtpInfo, localOtpInfo;
|
||||||
//load both files
|
//load both files
|
||||||
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
|
XmlSerializer xs = new XmlSerializer(typeof(OtpInfo));
|
||||||
localOtpInfo = (OtpInfo) xs.Deserialize(File.OpenRead(cachedFilePath));
|
using (var cacheStream = File.OpenRead(cachedFilePath))
|
||||||
|
{
|
||||||
|
localOtpInfo = (OtpInfo) xs.Deserialize(cacheStream);
|
||||||
|
}
|
||||||
using (Stream remoteStream = _cachedStorage.OpenFileForRead(ioc))
|
using (Stream remoteStream = _cachedStorage.OpenFileForRead(ioc))
|
||||||
{
|
{
|
||||||
remoteOtpInfo = (OtpInfo) xs.Deserialize(remoteStream);
|
remoteOtpInfo = (OtpInfo)xs.Deserialize(remoteStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
//see which OtpInfo has the bigger Counter value and use this one:
|
//see which OtpInfo has the bigger Counter value and use this one:
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>False</Optimize>
|
<Optimize>False</Optimize>
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
|
Loading…
Reference in New Issue
Block a user