mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-12-24 15:58:48 -05:00
added UI for opening a database with OTPs. Some TODOs and things not yet working!
This commit is contained in:
parent
59eace5834
commit
c686cbeeb3
@ -3,6 +3,7 @@ using Android.App;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using keepass2android.Io;
|
using keepass2android.Io;
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ namespace keepass2android
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the specified data as the currently open database, as unlocked.
|
/// Loads the specified data as the currently open database, as unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string s, string keyFile, ProgressDialogStatusLogger statusLogger);
|
void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the current database
|
/// Returns the current database
|
||||||
|
@ -107,16 +107,6 @@ namespace keepass2android.Io
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CompleteIoId()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? FileExists()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
return UrlUtil.StripExtension(
|
return UrlUtil.StripExtension(
|
||||||
@ -207,5 +197,19 @@ namespace keepass2android.Io
|
|||||||
parent += "/";
|
parent += "/";
|
||||||
return parent + newFilename;
|
return parent + newFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return IoUtil.GetParentPath(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
IOConnectionInfo res = folderPath.CloneDeep();
|
||||||
|
if (!res.Path.EndsWith("/"))
|
||||||
|
res.Path += "/";
|
||||||
|
res.Path += filename;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -400,16 +400,6 @@ namespace keepass2android.Io
|
|||||||
return new CachedWriteTransaction(ioc, useFileTransaction, this);
|
return new CachedWriteTransaction(ioc, useFileTransaction, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CompleteIoId()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? FileExists()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
return UrlUtil.StripExtension(
|
return UrlUtil.StripExtension(
|
||||||
@ -487,6 +477,54 @@ namespace keepass2android.Io
|
|||||||
return _cachedStorage.CreateFilePath(parent, newFilename);
|
return _cachedStorage.CreateFilePath(parent, newFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return _cachedStorage.GetParentPath(ioc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IOConnectionInfo res = _cachedStorage.GetFilePath(folderPath, filename);
|
||||||
|
//some file storage implementations require accessing the network to determine the file path (e.g. because
|
||||||
|
//they might contain file ids). In this case, we need to cache the result to enable cached access to such files
|
||||||
|
StoreFilePath(folderPath, filename, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
IOConnectionInfo res;
|
||||||
|
if (!TryGetCachedFilePath(folderPath, filename, out res)) throw;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StoreFilePath(IOConnectionInfo folderPath, string filename, IOConnectionInfo res)
|
||||||
|
{
|
||||||
|
File.WriteAllText(CachedFilePath(GetPseudoIoc(folderPath, filename)) + ".filepath", res.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOConnectionInfo GetPseudoIoc(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
IOConnectionInfo res = folderPath.CloneDeep();
|
||||||
|
if (!res.Path.EndsWith("/"))
|
||||||
|
res.Path += "/";
|
||||||
|
res.Path += filename;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCachedFilePath(IOConnectionInfo folderPath, string filename, out IOConnectionInfo res)
|
||||||
|
{
|
||||||
|
res = folderPath.CloneDeep();
|
||||||
|
string filePathCache = CachedFilePath(GetPseudoIoc(folderPath, filename)) + ".filepath";
|
||||||
|
if (!File.Exists(filePathCache))
|
||||||
|
return false;
|
||||||
|
res.Path = File.ReadAllText(filePathCache);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public string GetBaseVersionHash(IOConnectionInfo ioc)
|
public string GetBaseVersionHash(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
|
@ -82,19 +82,6 @@ namespace keepass2android.Io
|
|||||||
/// <param name="useFileTransaction">if true, force to use file system level transaction. This might be ignored if the file storage has built in transaction support</param>
|
/// <param name="useFileTransaction">if true, force to use file system level transaction. This might be ignored if the file storage has built in transaction support</param>
|
||||||
IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction);
|
IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// brings up a dialog to query credentials or something like this.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>true if success, false if error or cancelled by user</returns>
|
|
||||||
bool CompleteIoId( /*in/out ioId*/);
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks whether the given file exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>true if it exists, false if not. Null if the check couldn't be performed (e.g. because no credentials available or no connection established.)</returns>
|
|
||||||
bool? FileExists( /*ioId*/);
|
|
||||||
|
|
||||||
string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc);
|
string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -135,10 +122,10 @@ namespace keepass2android.Io
|
|||||||
void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId);
|
void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initiates the process for choosing a file in the given file storage.
|
/// Initiates the process for using a file in the given file storage.
|
||||||
/// The file storage should either call OnImmediateResult or StartFileUsageProcess
|
/// The file storage should either call OnImmediateResult or StartFileUsageProcess
|
||||||
/// If alwaysReturnSuccess is true, the activity should be finished with ResultCode Ok.
|
/// If alwaysReturnSuccess is true, the activity should be finished with ResultCode Ok.
|
||||||
/// This can make sense if a higher-level file storage has the file cached by still wants to
|
/// This can make sense if a higher-level file storage has the file cached but still wants to
|
||||||
/// give the cached storage the chance to initialize file access.
|
/// give the cached storage the chance to initialize file access.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode, bool alwaysReturnSuccess);
|
void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode, bool alwaysReturnSuccess);
|
||||||
@ -157,6 +144,17 @@ namespace keepass2android.Io
|
|||||||
//returns the path of a file "newFilename" in the folder "parent"
|
//returns the path of a file "newFilename" in the folder "parent"
|
||||||
//this may create the file if this is required to get a path (if a UUID is part of the file path)
|
//this may create the file if this is required to get a path (if a UUID is part of the file path)
|
||||||
string CreateFilePath(string parent, string newFilename);
|
string CreateFilePath(string parent, string newFilename);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns the parent folder of ioc
|
||||||
|
/// </summary>
|
||||||
|
IOConnectionInfo GetParentPath(IOConnectionInfo ioc);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns the file path of the file "filename" in the folderPath.
|
||||||
|
/// </summary>
|
||||||
|
/// The method may throw FileNotFoundException or not in case the file doesn't exist.
|
||||||
|
IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IWriteTransaction: IDisposable
|
public interface IWriteTransaction: IDisposable
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Java.IO;
|
using Java.IO;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
|
||||||
namespace keepass2android.Io
|
namespace keepass2android.Io
|
||||||
{
|
{
|
||||||
@ -30,5 +31,20 @@ namespace keepass2android.Io
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
var iocParent = ioc.CloneDeep();
|
||||||
|
if (iocParent.Path.EndsWith("/"))
|
||||||
|
iocParent.Path = iocParent.Path.Substring(0, iocParent.Path.Length - 1);
|
||||||
|
|
||||||
|
int slashPos = iocParent.Path.LastIndexOf("/", StringComparison.Ordinal);
|
||||||
|
if (slashPos == -1)
|
||||||
|
iocParent.Path = "";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iocParent.Path = iocParent.Path.Substring(0, slashPos);
|
||||||
|
}
|
||||||
|
return iocParent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,16 +151,6 @@ namespace keepass2android.Io
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CompleteIoId()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? FileExists()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
return UrlUtil.StripExtension(
|
return UrlUtil.StripExtension(
|
||||||
|
@ -86,25 +86,10 @@ namespace keepass2android
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
|
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, String password, String keyfile, ProgressDialogStatusLogger status)
|
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, ProgressDialogStatusLogger status)
|
||||||
{
|
{
|
||||||
PwDatabase pwDatabase = new PwDatabase();
|
PwDatabase pwDatabase = new PwDatabase();
|
||||||
|
|
||||||
CompositeKey compositeKey = new CompositeKey();
|
|
||||||
compositeKey.AddUserKey(new KcpPassword(password));
|
|
||||||
if (!String.IsNullOrEmpty(keyfile))
|
|
||||||
{
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
compositeKey.AddUserKey(new KcpKeyFile(keyfile));
|
|
||||||
} catch (Exception e)
|
|
||||||
{
|
|
||||||
Kp2aLog.Log(e.ToString());
|
|
||||||
throw new KeyFileException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
||||||
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
|
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
|
||||||
try
|
try
|
||||||
@ -115,7 +100,9 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
catch (InvalidCompositeKeyException)
|
catch (InvalidCompositeKeyException)
|
||||||
{
|
{
|
||||||
if ((password == "") && (keyfile != null))
|
KcpPassword passwordKey = (KcpPassword)compositeKey.GetUserKey(typeof(KcpPassword));
|
||||||
|
|
||||||
|
if ((passwordKey != null) && (passwordKey.Password.ReadString() == "") && (compositeKey.UserKeyCount > 1))
|
||||||
{
|
{
|
||||||
//if we don't get a password, we don't know whether this means "empty password" or "no password"
|
//if we don't get a password, we don't know whether this means "empty password" or "no password"
|
||||||
//retry without password:
|
//retry without password:
|
||||||
|
@ -19,6 +19,7 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
@ -26,18 +27,18 @@ namespace keepass2android
|
|||||||
public class LoadDb : RunnableOnFinish {
|
public class LoadDb : RunnableOnFinish {
|
||||||
private readonly IOConnectionInfo _ioc;
|
private readonly IOConnectionInfo _ioc;
|
||||||
private readonly Task<MemoryStream> _databaseData;
|
private readonly Task<MemoryStream> _databaseData;
|
||||||
private readonly String _pass;
|
private readonly CompositeKey _compositeKey;
|
||||||
private readonly String _key;
|
private readonly string _keyfileOrProvider;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private readonly bool _rememberKeyfile;
|
private readonly bool _rememberKeyfile;
|
||||||
|
|
||||||
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, String pass, String key, OnFinish finish): base(finish)
|
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(finish)
|
||||||
{
|
{
|
||||||
_app = app;
|
_app = app;
|
||||||
_ioc = ioc;
|
_ioc = ioc;
|
||||||
_databaseData = databaseData;
|
_databaseData = databaseData;
|
||||||
_pass = pass;
|
_compositeKey = compositeKey;
|
||||||
_key = key;
|
_keyfileOrProvider = keyfileOrProvider;
|
||||||
|
|
||||||
|
|
||||||
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
||||||
@ -50,8 +51,8 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
StatusLogger.UpdateMessage(UiStringKey.loading_database);
|
StatusLogger.UpdateMessage(UiStringKey.loading_database);
|
||||||
MemoryStream memoryStream = _databaseData == null ? null : _databaseData.Result;
|
MemoryStream memoryStream = _databaseData == null ? null : _databaseData.Result;
|
||||||
_app.LoadDatabase(_ioc, memoryStream, _pass, _key, StatusLogger);
|
_app.LoadDatabase(_ioc, memoryStream, _compositeKey, StatusLogger);
|
||||||
SaveFileData(_ioc, _key);
|
SaveFileData(_ioc, _keyfileOrProvider);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (KeyFileException)
|
catch (KeyFileException)
|
||||||
@ -88,13 +89,13 @@ namespace keepass2android
|
|||||||
Finish(true);
|
Finish(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveFileData(IOConnectionInfo ioc, String key) {
|
private void SaveFileData(IOConnectionInfo ioc, String keyfileOrProvider) {
|
||||||
|
|
||||||
if (!_rememberKeyfile)
|
if (!_rememberKeyfile)
|
||||||
{
|
{
|
||||||
key = "";
|
keyfileOrProvider = "";
|
||||||
}
|
}
|
||||||
_app.StoreOpenedFileAsRecent(ioc, key);
|
_app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,15 +85,6 @@ namespace Kp2aUnitTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CompleteIoId()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? FileExists()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@ namespace Kp2aUnitTests
|
|||||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile,
|
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile,
|
||||||
ProgressDialogStatusLogger statusLogger)
|
ProgressDialogStatusLogger statusLogger)
|
||||||
{
|
{
|
||||||
_db.LoadData(this, ioConnectionInfo, memoryStream, password, keyFile, statusLogger);
|
_db.LoadData(this, ioConnectionInfo, memoryStream, password, statusLogger);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
@ -16,13 +16,16 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
|
using Android.Database;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
|
using Java.Lang;
|
||||||
using Java.Net;
|
using Java.Net;
|
||||||
using Android.Preferences;
|
using Android.Preferences;
|
||||||
using Java.IO;
|
using Java.IO;
|
||||||
@ -31,8 +34,14 @@ using Android.Content.PM;
|
|||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
|
using OtpKeyProv;
|
||||||
using keepass2android.Io;
|
using keepass2android.Io;
|
||||||
|
using keepass2android.Utils;
|
||||||
|
using Exception = System.Exception;
|
||||||
using MemoryStream = System.IO.MemoryStream;
|
using MemoryStream = System.IO.MemoryStream;
|
||||||
|
using Object = Java.Lang.Object;
|
||||||
|
using Process = Android.OS.Process;
|
||||||
|
using String = System.String;
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
@ -41,6 +50,15 @@ namespace keepass2android
|
|||||||
Theme="@style/Base")]
|
Theme="@style/Base")]
|
||||||
|
|
||||||
public class PasswordActivity : LockingActivity {
|
public class PasswordActivity : LockingActivity {
|
||||||
|
|
||||||
|
enum KeyProviders
|
||||||
|
{
|
||||||
|
//int values correspond to indices in passwordSpinner
|
||||||
|
None = 0,
|
||||||
|
KeyFile = 1,
|
||||||
|
Otp = 2
|
||||||
|
}
|
||||||
|
|
||||||
bool _showPassword;
|
bool _showPassword;
|
||||||
|
|
||||||
public const String KeyDefaultFilename = "defaultFileName";
|
public const String KeyDefaultFilename = "defaultFileName";
|
||||||
@ -53,14 +71,37 @@ namespace keepass2android
|
|||||||
|
|
||||||
private const String ViewIntent = "android.intent.action.VIEW";
|
private const String ViewIntent = "android.intent.action.VIEW";
|
||||||
private const string ShowpasswordKey = "ShowPassword";
|
private const string ShowpasswordKey = "ShowPassword";
|
||||||
|
private const string KeyProviderIdOtp = "KP2A-OTP";
|
||||||
|
|
||||||
private Task<MemoryStream> _loadDbTask;
|
private Task<MemoryStream> _loadDbTask;
|
||||||
private IOConnectionInfo _ioConnection;
|
private IOConnectionInfo _ioConnection;
|
||||||
private String _keyFile;
|
private String _keyFileOrProvider;
|
||||||
|
|
||||||
|
internal AppTask AppTask;
|
||||||
|
private bool _killOnDestroy;
|
||||||
|
private string _password = "";
|
||||||
|
private const int RequestCodePrepareDbFile = 1000;
|
||||||
|
private const int RequestCodePrepareOtpAuxFile = 1001;
|
||||||
|
|
||||||
|
|
||||||
|
KeyProviders KeyProviderType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_keyFileOrProvider == null)
|
||||||
|
return KeyProviders.None;
|
||||||
|
if (_keyFileOrProvider == KeyProviderIdOtp)
|
||||||
|
return KeyProviders.Otp;
|
||||||
|
return KeyProviders.KeyFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool _rememberKeyfile;
|
private bool _rememberKeyfile;
|
||||||
ISharedPreferences _prefs;
|
ISharedPreferences _prefs;
|
||||||
|
|
||||||
private bool _starting;
|
private bool _starting;
|
||||||
|
private OtpInfo _otpInfo;
|
||||||
|
private readonly int[] _otpTextViewIds = new int[] {Resource.Id.otp1, Resource.Id.otp2, Resource.Id.otp3, Resource.Id.otp4, Resource.Id.otp5, Resource.Id.otp6};
|
||||||
|
|
||||||
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
public PasswordActivity (IntPtr javaReference, JniHandleOwnership transfer)
|
||||||
: base(javaReference, transfer)
|
: base(javaReference, transfer)
|
||||||
@ -170,6 +211,7 @@ namespace keepass2android
|
|||||||
KcpKeyFile kcpKeyfile = (KcpKeyFile)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpKeyFile));
|
KcpKeyFile kcpKeyfile = (KcpKeyFile)App.Kp2a.GetDb().KpDatabase.MasterKey.GetUserKey(typeof(KcpKeyFile));
|
||||||
|
|
||||||
SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path);
|
SetEditText(Resource.Id.pass_keyfile, kcpKeyfile.Path);
|
||||||
|
_keyFileOrProvider = kcpKeyfile.Path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
App.Kp2a.LockDatabase(false);
|
App.Kp2a.LockDatabase(false);
|
||||||
@ -186,18 +228,62 @@ namespace keepass2android
|
|||||||
|
|
||||||
EditText fn = (EditText) FindViewById(Resource.Id.pass_keyfile);
|
EditText fn = (EditText) FindViewById(Resource.Id.pass_keyfile);
|
||||||
fn.Text = filename;
|
fn.Text = filename;
|
||||||
|
_keyFileOrProvider = filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case (Result)FileStorageResults.FileUsagePrepared:
|
case (Result)FileStorageResults.FileUsagePrepared:
|
||||||
PeformLoadDatabase();
|
if (requestCode == RequestCodePrepareDbFile)
|
||||||
|
PerformLoadDatabase();
|
||||||
|
if (requestCode == RequestCodePrepareOtpAuxFile)
|
||||||
|
LoadOtpFile();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal AppTask AppTask;
|
private void LoadOtpFile()
|
||||||
private bool _killOnDestroy;
|
{
|
||||||
|
new LoadingDialog<object, object, object>(this, true,
|
||||||
|
//doInBackground
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
_otpInfo = OathHotpKeyProv.LoadOtpInfo(new KeyProviderQueryContext(_ioConnection, false, false));
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
//onPostExecute
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
if (_otpInfo == null)
|
||||||
|
{
|
||||||
|
Toast.MakeText(this, GetString(Resource.String.CouldntLoadOtpAuxFile), ToastLength.Long).Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FindViewById(Resource.Id.init_otp).Visibility = ViewStates.Gone;
|
||||||
|
FindViewById(Resource.Id.otpEntry).Visibility = ViewStates.Visible;
|
||||||
|
int c = 0;
|
||||||
|
foreach (int otpId in _otpTextViewIds)
|
||||||
|
{
|
||||||
|
c++;
|
||||||
|
var otpTextView = FindViewById<EditText>(otpId);
|
||||||
|
otpTextView.Text = "";
|
||||||
|
otpTextView.Hint = GetString(Resource.String.otp_hint, new Object[] {c});
|
||||||
|
otpTextView.SetFilters(new IInputFilter[] {new InputFilterLengthFilter((int)_otpInfo.OtpLength) });
|
||||||
|
if (c > _otpInfo.OtpsRequired)
|
||||||
|
{
|
||||||
|
otpTextView.Visibility = ViewStates.Gone;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
otpTextView.TextChanged += (sender, args) =>
|
||||||
|
{
|
||||||
|
UpdateOkButtonState();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).Execute();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnCreate(Bundle savedInstanceState)
|
protected override void OnCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
@ -245,15 +331,15 @@ namespace keepass2android
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_keyFile = GetKeyFile(_ioConnection.Path);
|
_keyFileOrProvider = GetKeyFile(_ioConnection.Path);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
SetIoConnectionFromIntent(_ioConnection, i);
|
SetIoConnectionFromIntent(_ioConnection, i);
|
||||||
_keyFile = i.GetStringExtra(KeyKeyfile);
|
_keyFileOrProvider = i.GetStringExtra(KeyKeyfile);
|
||||||
if (string.IsNullOrEmpty(_keyFile))
|
if (string.IsNullOrEmpty(_keyFileOrProvider))
|
||||||
{
|
{
|
||||||
_keyFile = GetKeyFile(_ioConnection.Path);
|
_keyFileOrProvider = GetKeyFile(_ioConnection.Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +357,19 @@ namespace keepass2android
|
|||||||
|
|
||||||
EditText passwordEdit = FindViewById<EditText>(Resource.Id.password);
|
EditText passwordEdit = FindViewById<EditText>(Resource.Id.password);
|
||||||
|
|
||||||
|
FindViewById<EditText>(Resource.Id.pass_keyfile).TextChanged +=
|
||||||
|
(sender, args) =>
|
||||||
|
{
|
||||||
|
_keyFileOrProvider = FindViewById<EditText>(Resource.Id.pass_keyfile).Text;
|
||||||
|
UpdateOkButtonState();
|
||||||
|
};
|
||||||
|
|
||||||
|
FindViewById<EditText>(Resource.Id.password).TextChanged +=
|
||||||
|
(sender, args) =>
|
||||||
|
{
|
||||||
|
_password = FindViewById<EditText>(Resource.Id.password).Text;
|
||||||
|
UpdateOkButtonState();
|
||||||
|
};
|
||||||
|
|
||||||
passwordEdit.RequestFocus();
|
passwordEdit.RequestFocus();
|
||||||
Window.SetSoftInputMode(SoftInput.StateVisible);
|
Window.SetSoftInputMode(SoftInput.StateVisible);
|
||||||
@ -279,10 +378,48 @@ namespace keepass2android
|
|||||||
confirmButton.Click += (sender, e) =>
|
confirmButton.Click += (sender, e) =>
|
||||||
{
|
{
|
||||||
App.Kp2a.GetFileStorage(_ioConnection)
|
App.Kp2a.GetFileStorage(_ioConnection)
|
||||||
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), _ioConnection, 0, false);
|
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), _ioConnection, RequestCodePrepareDbFile, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Spinner passwordModeSpinner = FindViewById<Spinner>(Resource.Id.password_mode_spinner);
|
||||||
|
if (passwordModeSpinner != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
UpdateKeyProviderUiState();
|
||||||
|
passwordModeSpinner.SetSelection((int) KeyProviderType);
|
||||||
|
passwordModeSpinner.ItemSelected += (sender, args) =>
|
||||||
|
{
|
||||||
|
switch (args.Position)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
_keyFileOrProvider = null;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
_keyFileOrProvider = "";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
_keyFileOrProvider = KeyProviderIdOtp;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Unexpected position "+args.Position+" / " + ((ICursor)((AdapterView)sender).GetItemAtPosition(args.Position)).GetString(1));
|
||||||
|
|
||||||
|
}
|
||||||
|
UpdateKeyProviderUiState();
|
||||||
|
};
|
||||||
|
FindViewById(Resource.Id.init_otp).Click += (sender, args) =>
|
||||||
|
{
|
||||||
|
App.Kp2a.GetOtpAuxFileStorage(_ioConnection)
|
||||||
|
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), _ioConnection, RequestCodePrepareOtpAuxFile, false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//android 2.x
|
||||||
|
//TODO test
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UpdateOkButtonState();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -327,15 +464,97 @@ namespace keepass2android
|
|||||||
RetrieveSettings();
|
RetrieveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PeformLoadDatabase()
|
private void UpdateOkButtonState()
|
||||||
{
|
{
|
||||||
String pass = GetEditText(Resource.Id.password);
|
switch (KeyProviderType)
|
||||||
String key = GetEditText(Resource.Id.pass_keyfile);
|
|
||||||
if (pass.Length == 0 && key.Length == 0)
|
|
||||||
{
|
{
|
||||||
ErrorMessage(Resource.String.error_nopass);
|
case KeyProviders.None:
|
||||||
|
FindViewById(Resource.Id.pass_ok).Enabled = true;
|
||||||
|
break;
|
||||||
|
case KeyProviders.KeyFile:
|
||||||
|
FindViewById(Resource.Id.pass_ok).Enabled = _keyFileOrProvider != "" || _password != "";
|
||||||
|
break;
|
||||||
|
case KeyProviders.Otp:
|
||||||
|
|
||||||
|
bool enabled = true;
|
||||||
|
if (_otpInfo == null)
|
||||||
|
enabled = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int c = 0;
|
||||||
|
foreach (int otpId in _otpTextViewIds)
|
||||||
|
{
|
||||||
|
c++;
|
||||||
|
var otpTextView = FindViewById<EditText>(otpId);
|
||||||
|
if ((c <= _otpInfo.OtpsRequired) && (otpTextView.Text == ""))
|
||||||
|
{
|
||||||
|
enabled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FindViewById(Resource.Id.pass_ok).Enabled = enabled;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateKeyProviderUiState()
|
||||||
|
{
|
||||||
|
FindViewById(Resource.Id.keyfileLine).Visibility = KeyProviderType == KeyProviders.KeyFile
|
||||||
|
? ViewStates.Visible
|
||||||
|
: ViewStates.Gone;
|
||||||
|
FindViewById(Resource.Id.otpView).Visibility = KeyProviderType == KeyProviders.Otp
|
||||||
|
? ViewStates.Visible
|
||||||
|
: ViewStates.Gone;
|
||||||
|
UpdateOkButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PerformLoadDatabase()
|
||||||
|
{
|
||||||
|
//no need to check for validity of password because if this method is called, the Ok button was enabled (i.e. there was a valid password)
|
||||||
|
CompositeKey compositeKey = new CompositeKey();
|
||||||
|
compositeKey.AddUserKey(new KcpPassword(_password));
|
||||||
|
if (KeyProviderType == KeyProviders.KeyFile)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compositeKey.AddUserKey(new KcpKeyFile(_keyFileOrProvider));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
|
throw new KeyFileException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (KeyProviderType == KeyProviders.Otp)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<string> lOtps = new List<string>();
|
||||||
|
foreach (int otpId in _otpTextViewIds)
|
||||||
|
{
|
||||||
|
string otpText = FindViewById<EditText>(otpId).Text;
|
||||||
|
if (!String.IsNullOrEmpty(otpText))
|
||||||
|
lOtps.Add(otpText);
|
||||||
|
}
|
||||||
|
CreateOtpSecret(lOtps);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
const string strMain = "Failed to create OTP key!";
|
||||||
|
const string strLine1 = "Make sure you've entered the correct OTPs.";
|
||||||
|
|
||||||
|
Toast.MakeText(this, strMain + " " + strLine1, ToastLength.Long).Show();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
compositeKey.AddUserKey(new KcpCustomKey(OathHotpKeyProv.Name, _otpInfo.Secret, true));
|
||||||
|
}
|
||||||
|
|
||||||
CheckBox cbQuickUnlock = (CheckBox) FindViewById(Resource.Id.enable_quickunlock);
|
CheckBox cbQuickUnlock = (CheckBox) FindViewById(Resource.Id.enable_quickunlock);
|
||||||
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
|
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
|
||||||
@ -345,7 +564,7 @@ namespace keepass2android
|
|||||||
MakePasswordMaskedOrVisible();
|
MakePasswordMaskedOrVisible();
|
||||||
|
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, pass, key, new AfterLoad(handler, this));
|
LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, compositeKey, _keyFileOrProvider, new AfterLoad(handler, this));
|
||||||
_loadDbTask = null; // prevent accidental re-use
|
_loadDbTask = null; // prevent accidental re-use
|
||||||
|
|
||||||
SetNewDefaultFile();
|
SetNewDefaultFile();
|
||||||
@ -353,6 +572,44 @@ namespace keepass2android
|
|||||||
new ProgressTask(App.Kp2a, this, task).Run();
|
new ProgressTask(App.Kp2a, this, task).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
TextView password = (TextView) FindViewById(Resource.Id.password);
|
TextView password = (TextView) FindViewById(Resource.Id.password);
|
||||||
@ -521,13 +778,9 @@ namespace keepass2android
|
|||||||
|
|
||||||
private String GetKeyFile(String filename) {
|
private String GetKeyFile(String filename) {
|
||||||
if ( _rememberKeyfile ) {
|
if ( _rememberKeyfile ) {
|
||||||
FileDbHelper dbHelp = App.Kp2a.FileDbHelper;
|
return App.Kp2a.FileDbHelper.GetKeyFileForFile(filename);
|
||||||
|
|
||||||
String keyfile = dbHelp.GetFileByName(filename);
|
|
||||||
|
|
||||||
return keyfile;
|
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,7 +794,8 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
FindViewById(Resource.Id.filename_group).Visibility = ViewStates.Visible;
|
FindViewById(Resource.Id.filename_group).Visibility = ViewStates.Visible;
|
||||||
}
|
}
|
||||||
SetEditText(Resource.Id.pass_keyfile, _keyFile);
|
if (KeyProviderType == KeyProviders.KeyFile)
|
||||||
|
SetEditText(Resource.Id.pass_keyfile, _keyFileOrProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
protected override void OnDestroy()
|
||||||
|
6687
src/keepass2android/Resources/Resource.designer.cs
generated
6687
src/keepass2android/Resources/Resource.designer.cs
generated
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="12dip"
|
android:layout_marginLeft="12dip"
|
||||||
android:layout_marginRight="12dip"
|
android:layout_marginRight="12dip"
|
||||||
android:layout_marginBottom="12dip">
|
android:layout_marginBottom="12dip"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/filename_group"
|
android:id="@+id/filename_group"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
@ -49,13 +51,18 @@
|
|||||||
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_below="@id/filename_group"
|
|
||||||
android:text="" />
|
android:text="" />
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/password_mode_spinner"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:entries="@array/password_modes"
|
||||||
|
/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
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:layout_below="@id/password_label"
|
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/password"
|
android:id="@+id/password"
|
||||||
@ -75,7 +82,6 @@
|
|||||||
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:layout_below="@id/passwordLine"
|
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/pass_keyfile"
|
android:id="@+id/pass_keyfile"
|
||||||
@ -89,23 +95,83 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:src="@drawable/ic_launcher_folder_small" />
|
android:src="@drawable/ic_launcher_folder_small" />
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/otpView"
|
||||||
|
android:layout_marginLeft="12dip"
|
||||||
|
android:layout_marginRight="12dip"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/init_otp"
|
||||||
|
android:text="@string/init_otp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/otpEntry"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/otp_expl"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/otp_explanation" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/otp1"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="93317749"
|
||||||
|
android:singleLine="true" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/otp2"
|
||||||
|
android:text="54719327"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/otp3"
|
||||||
|
android:text="49844651"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/otp4"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/otp5"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/otp6"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/pass_ok"
|
android:id="@+id/pass_ok"
|
||||||
android:text="@android:string/ok"
|
android:text="@android:string/ok"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"/>
|
||||||
android:layout_below="@id/keyfileLine" />
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/kill_app"
|
android:id="@+id/kill_app"
|
||||||
android:text="@string/kill_app_label"
|
android:text="@string/kill_app_label"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content" />
|
||||||
android:layout_below="@id/pass_ok" />
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/enable_quickunlock"
|
android:id="@+id/enable_quickunlock"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/kill_app"
|
|
||||||
android:text="@string/enable_quickunlock" />
|
android:text="@string/enable_quickunlock" />
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
@ -343,6 +343,14 @@
|
|||||||
|
|
||||||
<string name="error_adding_keyfile">Error while adding the keyfile!</string>
|
<string name="error_adding_keyfile">Error while adding the keyfile!</string>
|
||||||
|
|
||||||
|
<string name="init_otp">Enter OTPs…</string>
|
||||||
|
<string name="otp_explanation">Enter the next One-time-passwords (OTPs). Swipe your Yubikey NEO at the back of your device to enter via NFC.</string>
|
||||||
|
<string name="otp_hint">OTP %1$d</string>
|
||||||
|
<string name="CouldntLoadOtpAuxFile">Could not load auxiliary OTP file!</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="loading">Loading…</string>
|
||||||
|
|
||||||
<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">
|
||||||
@ -462,4 +470,9 @@ Initial public release
|
|||||||
<item>Remember username only</item>
|
<item>Remember username only</item>
|
||||||
<item>Remember username and password</item>
|
<item>Remember username and password</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="password_modes">
|
||||||
|
<item>Password only</item>
|
||||||
|
<item>Password + Key file</item>
|
||||||
|
<item>Password + OTP</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
164
src/keepass2android/Utils/LoadingDialog.cs
Normal file
164
src/keepass2android/Utils/LoadingDialog.cs
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Java.Lang;
|
||||||
|
using Exception = System.Exception;
|
||||||
|
using Object = Java.Lang.Object;
|
||||||
|
|
||||||
|
namespace keepass2android.Utils
|
||||||
|
{
|
||||||
|
public class LoadingDialog<TParams, TProgress, TResult> : AsyncTask<TParams, TProgress, TResult>
|
||||||
|
{
|
||||||
|
private readonly Context _context;
|
||||||
|
private readonly string _message;
|
||||||
|
private readonly bool _cancelable;
|
||||||
|
readonly Func<Object[], Object> _doInBackground;
|
||||||
|
readonly Action<Object> _onPostExecute;
|
||||||
|
|
||||||
|
private ProgressDialog mDialog;
|
||||||
|
/**
|
||||||
|
* Default is {@code 500}ms
|
||||||
|
*/
|
||||||
|
private int mDelayTime = 500;
|
||||||
|
/**
|
||||||
|
* Flag to use along with {@link #mDelayTime}
|
||||||
|
*/
|
||||||
|
private bool mFinished = false;
|
||||||
|
|
||||||
|
private Exception mLastException;
|
||||||
|
|
||||||
|
|
||||||
|
public LoadingDialog(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadingDialog(Context context, string message, bool cancelable, Func<Object[], Object> doInBackground,
|
||||||
|
Action<Object> onPostExecute)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_message = message;
|
||||||
|
_cancelable = cancelable;
|
||||||
|
_doInBackground = doInBackground;
|
||||||
|
_onPostExecute = onPostExecute;
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
mDialog = new ProgressDialog(_context);
|
||||||
|
mDialog.SetMessage(_message);
|
||||||
|
mDialog.Indeterminate = true;
|
||||||
|
mDialog.SetCancelable(_cancelable);
|
||||||
|
if (_cancelable)
|
||||||
|
{
|
||||||
|
mDialog.SetCanceledOnTouchOutside(true);
|
||||||
|
mDialog.CancelEvent += (sender, args) => mDialog.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadingDialog(Context context, bool cancelable, Func<Object[], Object> doInBackground, Action<Object> onPostExecute)
|
||||||
|
{
|
||||||
|
_message = context.GetString(Resource.String.loading);
|
||||||
|
_context = context;
|
||||||
|
_cancelable = cancelable;
|
||||||
|
_doInBackground = doInBackground;
|
||||||
|
_onPostExecute = onPostExecute;
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPreExecute()
|
||||||
|
{
|
||||||
|
new Handler().PostDelayed(() =>
|
||||||
|
{
|
||||||
|
if (!mFinished)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* sometime the activity has been finished before we
|
||||||
|
* show this dialog, it will raise error
|
||||||
|
*/
|
||||||
|
mDialog.Show();
|
||||||
|
}
|
||||||
|
catch (Exception t)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(t.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
, mDelayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you override this method, you must call {@code super.onCancelled()} at
|
||||||
|
* beginning of the method.
|
||||||
|
*/
|
||||||
|
protected override void OnCancelled() {
|
||||||
|
DoFinish();
|
||||||
|
base.OnCancelled();
|
||||||
|
}// onCancelled()
|
||||||
|
|
||||||
|
private void DoFinish() {
|
||||||
|
mFinished = true;
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* Sometime the activity has been finished before we dismiss this
|
||||||
|
* dialog, it will raise error.
|
||||||
|
*/
|
||||||
|
mDialog.Dismiss();
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
|
}
|
||||||
|
}// doFinish()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets last exception. This method is useful in case an exception raises
|
||||||
|
* inside {@link #doInBackground(Void...)}
|
||||||
|
*
|
||||||
|
* @param t
|
||||||
|
* {@link Throwable}
|
||||||
|
*/
|
||||||
|
protected void SetLastException(Exception e) {
|
||||||
|
mLastException = e;
|
||||||
|
}// setLastException()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets last exception.
|
||||||
|
*
|
||||||
|
* @return {@link Throwable}
|
||||||
|
*/
|
||||||
|
protected Exception GetLastException() {
|
||||||
|
return mLastException;
|
||||||
|
}// getLastException()
|
||||||
|
|
||||||
|
|
||||||
|
protected override Object DoInBackground(params Object[] @params)
|
||||||
|
{
|
||||||
|
return _doInBackground(@params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TResult RunInBackground(params TParams[] @params)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPostExecute(Object result)
|
||||||
|
{
|
||||||
|
DoFinish();
|
||||||
|
|
||||||
|
if (_onPostExecute != null)
|
||||||
|
_onPostExecute(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
@ -39,9 +38,8 @@ namespace OtpKeyProv
|
|||||||
FmtHex, FmtBase64, FmtBase32, FmtUtf8, FmtDec
|
FmtHex, FmtBase64, FmtBase32, FmtUtf8, FmtDec
|
||||||
};
|
};
|
||||||
|
|
||||||
public static OtpDataFmt? GetOtpDataFormat(ComboBox cmb)
|
public static OtpDataFmt? GetOtpDataFormat(String strFmt)
|
||||||
{
|
{
|
||||||
string strFmt = (cmb.SelectedItem as string);
|
|
||||||
if(strFmt == null) return null; // No assert
|
if(strFmt == null) return null; // No assert
|
||||||
|
|
||||||
if(strFmt == FmtHex) return OtpDataFmt.Hex;
|
if(strFmt == FmtHex) return OtpDataFmt.Hex;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
This file was modified my Philipp Crocoll, 2013. Based on:
|
||||||
|
|
||||||
OtpKeyProv Plugin
|
OtpKeyProv Plugin
|
||||||
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
|
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
|
||||||
@ -19,81 +21,49 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using OtpKeyProv.Forms;
|
|
||||||
|
|
||||||
using KeePass.UI;
|
|
||||||
|
|
||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
|
using keepass2android;
|
||||||
|
using keepass2android.Io;
|
||||||
|
|
||||||
namespace OtpKeyProv
|
namespace OtpKeyProv
|
||||||
{
|
{
|
||||||
public sealed class OathHotpKeyProv : KeyProvider
|
public sealed class OathHotpKeyProv
|
||||||
|
/*removed base class KeyProvider because "synchronous" interface is not suitable on Android*/
|
||||||
{
|
{
|
||||||
private const string AuxFileExt = ".otp.xml";
|
private const string AuxFileExt = ".otp.xml";
|
||||||
private const string ProvType = "OATH HOTP / RFC 4226";
|
private const string ProvType = "OATH HOTP / RFC 4226";
|
||||||
private const string ProvVersion = "2.0"; // File version, not OtpKeyProv version
|
private const string ProvVersion = "2.0"; // File version, not OtpKeyProv version
|
||||||
|
|
||||||
public override string Name
|
public static string Name
|
||||||
{
|
{
|
||||||
get { return "One-Time Passwords (OATH HOTP)"; }
|
get { return "One-Time Passwords (OATH HOTP)"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool SecureDesktopCompatible
|
|
||||||
{
|
|
||||||
get { return true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override byte[] GetKey(KeyProviderQueryContext ctx)
|
public const string ShortProductName = "OtpKeyProv";
|
||||||
{
|
public const string ProductName = "OtpKeyProv KeePass Plugin";
|
||||||
try
|
|
||||||
{
|
|
||||||
if(ctx.CreatingNewKey) return Create(ctx);
|
|
||||||
return Open(ctx);
|
|
||||||
}
|
|
||||||
catch(Exception ex) { MessageService.ShowWarning(ex.Message); }
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IOConnectionInfo GetAuxFileIoc(KeyProviderQueryContext ctx)
|
private static IOConnectionInfo GetAuxFileIoc(KeyProviderQueryContext ctx)
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = ctx.DatabaseIOInfo.CloneDeep();
|
IOConnectionInfo ioc = ctx.DatabaseIOInfo.CloneDeep();
|
||||||
ioc.Path = UrlUtil.StripExtension(ioc.Path) + AuxFileExt;
|
IFileStorage fileStorage = App.Kp2a.GetOtpAuxFileStorage(ioc);
|
||||||
return ioc;
|
IOConnectionInfo iocAux = fileStorage.GetFilePath(fileStorage.GetParentPath(ioc),
|
||||||
|
fileStorage.GetFilenameWithoutPathAndExt(ioc) + AuxFileExt);
|
||||||
|
|
||||||
|
return iocAux;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] Create(KeyProviderQueryContext ctx)
|
public static OtpInfo LoadOtpInfo(KeyProviderQueryContext ctx)
|
||||||
{
|
{
|
||||||
IOConnectionInfo iocPrev = GetAuxFileIoc(ctx);
|
return OtpInfo.Load(GetAuxFileIoc(ctx));
|
||||||
OtpInfo otpInfo = OtpInfo.Load(iocPrev);
|
|
||||||
if(otpInfo == null) otpInfo = new OtpInfo();
|
|
||||||
|
|
||||||
OtpKeyCreationForm dlg = new OtpKeyCreationForm();
|
|
||||||
dlg.InitEx(otpInfo, ctx);
|
|
||||||
|
|
||||||
if(UIUtil.ShowDialogAndDestroy(dlg) != DialogResult.OK)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if(!CreateAuxFile(otpInfo, ctx)) return null;
|
|
||||||
return otpInfo.Secret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] Open(KeyProviderQueryContext ctx)
|
/*
|
||||||
|
private static byte[] Open(KeyProviderQueryContext ctx, OtpInfo otpInfo)
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = GetAuxFileIoc(ctx);
|
|
||||||
OtpInfo otpInfo = OtpInfo.Load(ioc);
|
|
||||||
if(otpInfo == null)
|
|
||||||
{
|
|
||||||
MessageService.ShowWarning("Failed to load auxiliary OTP info file:",
|
|
||||||
ioc.GetDisplayName());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if(otpInfo.Type != ProvType)
|
if(otpInfo.Type != ProvType)
|
||||||
{
|
{
|
||||||
MessageService.ShowWarning("Unknown OTP generator type!");
|
MessageService.ShowWarning("Unknown OTP generator type!");
|
||||||
@ -108,13 +78,72 @@ namespace OtpKeyProv
|
|||||||
if(!CreateAuxFile(otpInfo, ctx)) return null;
|
if(!CreateAuxFile(otpInfo, ctx)) return null;
|
||||||
return otpInfo.Secret;
|
return otpInfo.Secret;
|
||||||
}
|
}
|
||||||
|
* */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the "Secret" field in otpInfo based on the list of entered OTPs (lOtps) or the entered secret itself which is in format fmt
|
||||||
|
/// </summary>
|
||||||
|
/// based on the code in OtpKeyPromptForm.cs
|
||||||
|
public void SetSecret(OtpInfo otpInfo, List<string> lOtps, string secret, OtpDataFmt? fmt)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] pbSecret = EncodingUtil.ParseKey(secret,
|
||||||
|
(fmt.HasValue ? fmt.Value : OtpDataFmt.Hex));
|
||||||
|
if (pbSecret != null)
|
||||||
|
{
|
||||||
|
otpInfo.Secret = pbSecret;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
//todo
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static bool CreateAuxFile(OtpInfo otpInfo,
|
private static bool CreateAuxFile(OtpInfo otpInfo,
|
||||||
KeyProviderQueryContext ctx)
|
KeyProviderQueryContext ctx)
|
||||||
{
|
{
|
||||||
otpInfo.Type = ProvType;
|
otpInfo.Type = ProvType;
|
||||||
otpInfo.Version = ProvVersion;
|
otpInfo.Version = ProvVersion;
|
||||||
otpInfo.Generator = OtpKeyProvExt.ProductName;
|
otpInfo.Generator = ProductName;
|
||||||
|
|
||||||
otpInfo.EncryptSecret();
|
otpInfo.EncryptSecret();
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
This file was modified my Philipp Crocoll, 2013. Based on:
|
||||||
|
|
||||||
OtpKeyProv Plugin
|
OtpKeyProv Plugin
|
||||||
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
|
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ using KeePassLib.Cryptography;
|
|||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
|
using keepass2android;
|
||||||
|
|
||||||
namespace OtpKeyProv
|
namespace OtpKeyProv
|
||||||
{
|
{
|
||||||
@ -168,12 +171,15 @@ namespace OtpKeyProv
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sIn = IOConnection.OpenRead(ioc);
|
sIn = App.Kp2a.GetOtpAuxFileStorage(ioc).OpenFileForRead(ioc);
|
||||||
|
|
||||||
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
|
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
|
||||||
return (OtpInfo) xs.Deserialize(sIn);
|
return (OtpInfo) xs.Deserialize(sIn);
|
||||||
}
|
}
|
||||||
catch(Exception) { }
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log(e.ToString());
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if(sIn != null) sIn.Close();
|
if(sIn != null) sIn.Close();
|
||||||
@ -188,20 +194,23 @@ namespace OtpKeyProv
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sOut = IOConnection.OpenWrite(ioc);
|
using (var trans = App.Kp2a.GetOtpAuxFileStorage(ioc)
|
||||||
|
.OpenWriteTransaction(ioc, App.Kp2a.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
|
||||||
|
{
|
||||||
XmlWriterSettings xws = new XmlWriterSettings();
|
XmlWriterSettings xws = new XmlWriterSettings();
|
||||||
xws.CloseOutput = true;
|
xws.CloseOutput = true;
|
||||||
xws.Encoding = StrUtil.Utf8;
|
xws.Encoding = StrUtil.Utf8;
|
||||||
xws.Indent = true;
|
xws.Indent = true;
|
||||||
xws.IndentChars = "\t";
|
xws.IndentChars = "\t";
|
||||||
|
|
||||||
XmlWriter xw = XmlWriter.Create(sOut, xws);
|
XmlWriter xw = XmlWriter.Create(trans.OpenFile(), xws);
|
||||||
|
|
||||||
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
|
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
|
||||||
xs.Serialize(xw, otpInfo);
|
xs.Serialize(xw, otpInfo);
|
||||||
|
|
||||||
xw.Close();
|
xw.Close();
|
||||||
|
trans.CommitWrite();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch(Exception) { Debug.Assert(false); }
|
catch(Exception) { Debug.Assert(false); }
|
||||||
|
@ -107,9 +107,9 @@ namespace keepass2android
|
|||||||
Application.Context.SendBroadcast(new Intent(Intents.DatabaseLocked));
|
Application.Context.SendBroadcast(new Intent(Intents.DatabaseLocked));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile, ProgressDialogStatusLogger statusLogger)
|
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compositeKey, ProgressDialogStatusLogger statusLogger)
|
||||||
{
|
{
|
||||||
_db.LoadData(this, ioConnectionInfo, memoryStream, password, keyFile, statusLogger);
|
_db.LoadData(this, ioConnectionInfo, memoryStream, compositeKey, statusLogger);
|
||||||
|
|
||||||
UpdateOngoingNotification();
|
UpdateOngoingNotification();
|
||||||
}
|
}
|
||||||
@ -354,10 +354,9 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
||||||
|
|
||||||
var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
|
if (DatabaseCacheEnabled)
|
||||||
|
|
||||||
if (prefs.GetBoolean(Application.Context.Resources.GetString(Resource.String.UseOfflineCache_key), true))
|
|
||||||
{
|
{
|
||||||
|
//TODO
|
||||||
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
|
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -494,6 +493,42 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
return GetFileStorage(new IOConnectionInfo() {Path = protocolId + "://"});
|
return GetFileStorage(new IOConnectionInfo() {Path = protocolId + "://"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns a file storage object to be used when accessing the auxiliary OTP file
|
||||||
|
/// </summary>
|
||||||
|
/// The reason why this requires a different file storage is the different caching behavior.
|
||||||
|
public IFileStorage GetOtpAuxFileStorage(IOConnectionInfo iocInfo)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (iocInfo.IsLocalFile())
|
||||||
|
return new BuiltInFileStorage();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
||||||
|
|
||||||
|
|
||||||
|
if (DatabaseCacheEnabled)
|
||||||
|
{
|
||||||
|
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return innerFileStorage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool DatabaseCacheEnabled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
|
||||||
|
bool cacheEnabled = prefs.GetBoolean(Application.Context.Resources.GetString(Resource.String.UseOfflineCache_key),
|
||||||
|
true);
|
||||||
|
return cacheEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,12 +230,12 @@ namespace keepass2android
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String GetFileByName(String name) {
|
public String GetKeyFileForFile(String name) {
|
||||||
ICursor cursor = mDb.Query(true, FileTable, GetColumnList(),
|
ICursor cursor = mDb.Query(true, FileTable, GetColumnList(),
|
||||||
KeyFileFilename + "= ?", new[] {name}, null, null, null, null);
|
KeyFileFilename + "= ?", new[] {name}, null, null, null, null);
|
||||||
|
|
||||||
if ( cursor == null ) {
|
if ( cursor == null ) {
|
||||||
return "";
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String keyfileFilename;
|
String keyfileFilename;
|
||||||
@ -244,9 +244,11 @@ namespace keepass2android
|
|||||||
keyfileFilename = cursor.GetString(cursor.GetColumnIndexOrThrow(KeyFileKeyfile));
|
keyfileFilename = cursor.GetString(cursor.GetColumnIndexOrThrow(KeyFileKeyfile));
|
||||||
} else {
|
} else {
|
||||||
// Cursor is empty
|
// Cursor is empty
|
||||||
keyfileFilename = "";
|
keyfileFilename = null;
|
||||||
}
|
}
|
||||||
cursor.Close();
|
cursor.Close();
|
||||||
|
if (keyfileFilename == "")
|
||||||
|
return null;
|
||||||
return keyfileFilename;
|
return keyfileFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ namespace keepass2android
|
|||||||
#if !EXCLUDE_FILECHOOSER
|
#if !EXCLUDE_FILECHOOSER
|
||||||
StartFileChooser(ioc.Path);
|
StartFileChooser(ioc.Path);
|
||||||
#else
|
#else
|
||||||
LaunchPasswordActivityForIoc(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/keepass.kdbx"});
|
LaunchPasswordActivityForIoc(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/yubi2.kdbx"});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
|
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
|
||||||
|
@ -82,6 +82,10 @@
|
|||||||
<Reference Include="Mono.Android.Support.v4" />
|
<Reference Include="Mono.Android.Support.v4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="addons\OtpKeyProv\EncodingUtil.cs" />
|
||||||
|
<Compile Include="addons\OtpKeyProv\OathHotpKeyProv.cs" />
|
||||||
|
<Compile Include="addons\OtpKeyProv\OtpInfo.cs" />
|
||||||
|
<Compile Include="addons\OtpKeyProv\OtpUtil.cs" />
|
||||||
<Compile Include="app\NoFileStorageFoundException.cs" />
|
<Compile Include="app\NoFileStorageFoundException.cs" />
|
||||||
<Compile Include="CreateDatabaseActivity.cs" />
|
<Compile Include="CreateDatabaseActivity.cs" />
|
||||||
<Compile Include="fileselect\FileChooserFileProvider.cs" />
|
<Compile Include="fileselect\FileChooserFileProvider.cs" />
|
||||||
@ -102,6 +106,7 @@
|
|||||||
<Compile Include="search\SearchProvider.cs" />
|
<Compile Include="search\SearchProvider.cs" />
|
||||||
<Compile Include="services\OngoingNotificationsService.cs" />
|
<Compile Include="services\OngoingNotificationsService.cs" />
|
||||||
<Compile Include="settings\DatabaseSettingsActivity.cs" />
|
<Compile Include="settings\DatabaseSettingsActivity.cs" />
|
||||||
|
<Compile Include="Utils\LoadingDialog.cs" />
|
||||||
<Compile Include="Utils\Util.cs" />
|
<Compile Include="Utils\Util.cs" />
|
||||||
<Compile Include="intents\Intents.cs" />
|
<Compile Include="intents\Intents.cs" />
|
||||||
<Compile Include="timeout\TimeoutHelper.cs" />
|
<Compile Include="timeout\TimeoutHelper.cs" />
|
||||||
@ -629,7 +634,9 @@
|
|||||||
<AndroidResource Include="Resources\layout-v14\SaveButton.xml" />
|
<AndroidResource Include="Resources\layout-v14\SaveButton.xml" />
|
||||||
<AndroidResource Include="Resources\layout-v14\generate_password.xml" />
|
<AndroidResource Include="Resources\layout-v14\generate_password.xml" />
|
||||||
<AndroidResource Include="Resources\layout-v14\icon_picker.xml" />
|
<AndroidResource Include="Resources\layout-v14\icon_picker.xml" />
|
||||||
<AndroidResource Include="Resources\layout-v14\password.xml" />
|
<AndroidResource Include="Resources\layout-v14\password.xml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</AndroidResource>
|
||||||
<AndroidResource Include="Resources\layout\InViewButton.xml" />
|
<AndroidResource Include="Resources\layout\InViewButton.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\collections_collection.png" />
|
<AndroidResource Include="Resources\drawable\collections_collection.png" />
|
||||||
<AndroidResource Include="Resources\drawable\collections_new_label.png" />
|
<AndroidResource Include="Resources\drawable\collections_new_label.png" />
|
||||||
|
Loading…
Reference in New Issue
Block a user