fixed problems with OTP, completed implementation to work with cloud storage

This commit is contained in:
Philipp Crocoll 2013-11-22 21:47:13 +01:00
parent aeaba47573
commit 3fde2c9846
12 changed files with 4849 additions and 2013 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

@ -45,7 +45,7 @@ namespace keepass2android
}
public override void Run ()
public override void Run()
{
try
{

View File

@ -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)
{
Handler.Post(DoSyncOtpAuxFile);
}
else
DoSyncOtpAuxFile();
base.Run();
}
private void DoSyncOtpAuxFile()
{
StatusLogger.UpdateMessage(UiStringKey.SynchronizingOtpAuxFile);
try
{
//simply open the file. The file storage does a complete sync.
using (App.Kp2a.GetOtpAuxFileStorage(_ioc).OpenFileForRead(_ioc))
{
}
Finish(true);
}
catch (Exception e)
{
Finish(false, e.Message);
}
}
}
}
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();

View File

@ -268,8 +268,16 @@ namespace keepass2android
new LoadingDialog<object, object, object>(this, true,
//doInBackground
delegate
{
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();
@ -1119,26 +1095,8 @@ namespace keepass2android
}
public override void Run() {
if (_act.KeyProviderType == KeyProviders.Otp)
public override void Run()
{
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 )
{
_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
}
@ -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();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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">

View File

@ -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();
}
}
}
}

View File

@ -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:

View File

@ -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>