mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-12-23 07:28:48 -05: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>
|
||||
<Optimize>False</Optimize>
|
||||
<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>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>False</ConsolePause>
|
||||
|
@ -275,6 +275,27 @@ namespace keepass2android.Io
|
||||
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)
|
||||
{
|
||||
return new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||
|
@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<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>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
@ -45,7 +45,7 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
|
||||
public override void Run ()
|
||||
public override void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -368,56 +368,68 @@ namespace keepass2android
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
class SyncOtpAuxFile: OnFinish
|
||||
public class SyncOtpAuxFile: RunnableOnFinish
|
||||
{
|
||||
private readonly IOConnectionInfo _ioc;
|
||||
|
||||
public SyncOtpAuxFile(IOConnectionInfo ioc)
|
||||
public SyncOtpAuxFile(IOConnectionInfo ioc) : base(null)
|
||||
{
|
||||
_ioc = ioc;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
if (Handler != null)
|
||||
StatusLogger.UpdateMessage(UiStringKey.SynchronizingOtpAuxFile);
|
||||
try
|
||||
{
|
||||
Handler.Post(DoSyncOtpAuxFile);
|
||||
//simply open the file. The file storage does a complete sync.
|
||||
using (App.Kp2a.GetOtpAuxFileStorage(_ioc).OpenFileForRead(_ioc))
|
||||
{
|
||||
}
|
||||
|
||||
Finish(true);
|
||||
}
|
||||
else
|
||||
DoSyncOtpAuxFile();
|
||||
base.Run();
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Finish(false, e.Message);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void DoSyncOtpAuxFile()
|
||||
{
|
||||
StatusLogger.UpdateMessage(UiStringKey.SynchronizingOtpAuxFile);
|
||||
//simply open the file. The file storage does a complete sync.
|
||||
using (App.Kp2a.GetOtpAuxFileStorage(_ioc).OpenFileForRead(_ioc))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Synchronize()
|
||||
{
|
||||
var filestorage = App.Kp2a.GetFileStorage(App.Kp2a.GetDb().Ioc);
|
||||
RunnableOnFinish task;
|
||||
ActionOnFinish onFinishShowMessage = new ActionOnFinish((success, message) =>
|
||||
OnFinish onFinish = new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!String.IsNullOrEmpty(message))
|
||||
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)
|
||||
{
|
||||
|
||||
task = new SynchronizeCachedDatabase(this, App.Kp2a, onFinishShowMessage);
|
||||
task = new SynchronizeCachedDatabase(this, App.Kp2a, onFinish);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
task = new CheckDatabaseForChanges(this, App.Kp2a, onFinishShowMessage);
|
||||
task = new CheckDatabaseForChanges(this, App.Kp2a, onFinish);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var progressTask = new ProgressTask(App.Kp2a, this, task);
|
||||
progressTask.Run();
|
||||
|
||||
|
@ -269,7 +269,15 @@ namespace keepass2android
|
||||
//doInBackground
|
||||
delegate
|
||||
{
|
||||
_otpInfo = OathHotpKeyProv.LoadOtpInfo(new KeyProviderQueryContext(_ioConnection, false, false));
|
||||
try
|
||||
{
|
||||
_otpInfo = OathHotpKeyProv.LoadOtpInfo(new KeyProviderQueryContext(_ioConnection, false, false));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log(e.ToString());
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
//onPostExecute
|
||||
@ -694,8 +702,8 @@ namespace keepass2android
|
||||
|
||||
try
|
||||
{
|
||||
var lOtps = GetOtpsFromUI();
|
||||
CreateOtpSecret(lOtps);
|
||||
var lOtps = GetOtpsFromUi();
|
||||
OathHotpKeyProv.CreateOtpSecret(lOtps, _otpInfo);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@ -731,7 +739,12 @@ namespace keepass2android
|
||||
MakePasswordMaskedOrVisible();
|
||||
|
||||
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
|
||||
|
||||
SetNewDefaultFile();
|
||||
@ -739,7 +752,7 @@ namespace keepass2android
|
||||
new ProgressTask(App.Kp2a, this, task).Run();
|
||||
}
|
||||
|
||||
private List<string> GetOtpsFromUI()
|
||||
private List<string> GetOtpsFromUi()
|
||||
{
|
||||
List<string> lOtps = new List<string>();
|
||||
foreach (int otpId in _otpTextViewIds)
|
||||
@ -751,43 +764,6 @@ namespace keepass2android
|
||||
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()
|
||||
{
|
||||
@ -900,7 +876,7 @@ namespace keepass2android
|
||||
outState.PutStringArrayList(PendingOtpsKey, _pendingOtps);
|
||||
if (_otpInfo != null)
|
||||
{
|
||||
outState.PutStringArrayList(EnteredOtpsKey, GetOtpsFromUI());
|
||||
outState.PutStringArrayList(EnteredOtpsKey, GetOtpsFromUi());
|
||||
|
||||
var sw = new StringWriter();
|
||||
|
||||
@ -1117,28 +1093,10 @@ namespace keepass2android
|
||||
{
|
||||
_act = act;
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
if ( Success )
|
||||
{
|
||||
_act.SetEditText(Resource.Id.password, "");
|
||||
@ -1150,8 +1108,6 @@ namespace keepass2android
|
||||
|
||||
_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
|
||||
}
|
||||
@ -1161,8 +1117,46 @@ 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:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dip"
|
||||
android:text="@string/master_key_type" />
|
||||
<Spinner
|
||||
android:id="@+id/password_mode_spinner"
|
||||
@ -68,6 +69,7 @@ android:layout_height="match_parent"
|
||||
android:id="@+id/passwordLine"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
@ -76,6 +78,7 @@ android:layout_height="match_parent"
|
||||
android:singleLine="true"
|
||||
android:inputType="textPassword"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="bottom"
|
||||
android:hint="@string/hint_login_pass" />
|
||||
<ImageButton
|
||||
android:id="@+id/toggle_password"
|
||||
@ -87,6 +90,7 @@ android:layout_height="match_parent"
|
||||
android:id="@+id/keyfileLine"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
<EditText
|
||||
android:id="@+id/pass_keyfile"
|
||||
@ -94,6 +98,7 @@ android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="bottom"
|
||||
android:hint="@string/entry_keyfile" />
|
||||
<ImageButton
|
||||
android:id="@+id/browse_button"
|
||||
|
@ -371,10 +371,13 @@
|
||||
<string name="ChangeLog_title">Change log</string>
|
||||
|
||||
<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
|
||||
* Integrated Keepass 2.24 library\n
|
||||
* Added option to kill the app process (see settings)\n
|
||||
* Bug fixes\n
|
||||
</string>
|
||||
|
||||
<string name="ChangeLog_0_9_1">
|
||||
|
@ -137,7 +137,7 @@ namespace OtpKeyProv
|
||||
|
||||
|
||||
public static bool CreateAuxFile(OtpInfo otpInfo,
|
||||
KeyProviderQueryContext ctx)
|
||||
KeyProviderQueryContext ctx, IOConnectionInfo auxFileIoc)
|
||||
{
|
||||
otpInfo.Type = ProvType;
|
||||
otpInfo.Version = ProvVersion;
|
||||
@ -145,15 +145,54 @@ namespace OtpKeyProv
|
||||
|
||||
otpInfo.EncryptSecret();
|
||||
|
||||
IOConnectionInfo ioc = GetAuxFileIoc(ctx);
|
||||
if(!OtpInfo.Save(ioc, otpInfo))
|
||||
if(!OtpInfo.Save(auxFileIoc, otpInfo))
|
||||
{
|
||||
MessageService.ShowWarning("Failed to save auxiliary OTP info file:",
|
||||
ioc.GetDisplayName());
|
||||
auxFileIoc.GetDisplayName());
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
//load both files
|
||||
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
|
||||
localOtpInfo = (OtpInfo) xs.Deserialize(File.OpenRead(cachedFilePath));
|
||||
XmlSerializer xs = new XmlSerializer(typeof(OtpInfo));
|
||||
using (var cacheStream = File.OpenRead(cachedFilePath))
|
||||
{
|
||||
localOtpInfo = (OtpInfo) xs.Deserialize(cacheStream);
|
||||
}
|
||||
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:
|
||||
|
@ -24,7 +24,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<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>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>False</ConsolePause>
|
||||
|
Loading…
Reference in New Issue
Block a user