mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-12-23 07:28:48 -05:00
Merge branch 'Branch_KeeChallenge'
Conflicts: src/keepass2android/Resources/Resource.designer.cs src/keepass2android/keepass2android.csproj
This commit is contained in:
commit
12e76bd651
205
src/keepass2android/ChallengeInfo.cs
Normal file
205
src/keepass2android/ChallengeInfo.cs
Normal file
@ -0,0 +1,205 @@
|
||||
//
|
||||
// ChallengeInfo.cs
|
||||
//
|
||||
// Author:
|
||||
// Ben.Rush <>
|
||||
//
|
||||
// Copyright (c) 2014 Ben.Rush
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
using keepass2android;
|
||||
using KeePassLib.Serialization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace KeeChallenge
|
||||
{
|
||||
public class ChallengeInfo
|
||||
{
|
||||
public byte[] EncryptedSecret {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public byte[] IV {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public byte[] Challenge {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public byte[] Verification {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private ChallengeInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public ChallengeInfo(byte[] encryptedSecret, byte[] iv, byte[] challenge, byte[] verification)
|
||||
{
|
||||
EncryptedSecret = encryptedSecret;
|
||||
IV = iv;
|
||||
Challenge = challenge;
|
||||
Verification = verification;
|
||||
}
|
||||
|
||||
public static ChallengeInfo Load(IOConnectionInfo ioc)
|
||||
{
|
||||
Stream sIn = null;
|
||||
ChallengeInfo inf = new ChallengeInfo();
|
||||
try
|
||||
{
|
||||
sIn = App.Kp2a.GetOtpAuxFileStorage(ioc).OpenFileForRead(ioc);
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(typeof (ChallengeInfo));
|
||||
if (!inf.LoadStream(sIn)) return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log(e.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(sIn != null) sIn.Close();
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
private bool LoadStream(Stream AuxFile)
|
||||
{
|
||||
//read file
|
||||
XmlReader xml;
|
||||
try
|
||||
{
|
||||
XmlReaderSettings settings = new XmlReaderSettings();
|
||||
settings.CloseInput = true;
|
||||
xml = XmlReader.Create(AuxFile,settings);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (xml.Read())
|
||||
{
|
||||
if (xml.IsStartElement())
|
||||
{
|
||||
switch (xml.Name)
|
||||
{
|
||||
case "encrypted":
|
||||
xml.Read();
|
||||
EncryptedSecret = Convert.FromBase64String(xml.Value.Trim());
|
||||
break;
|
||||
case "iv":
|
||||
xml.Read();
|
||||
IV = Convert.FromBase64String(xml.Value.Trim());
|
||||
break;
|
||||
case "challenge":
|
||||
xml.Read();
|
||||
Challenge = Convert.FromBase64String(xml.Value.Trim());
|
||||
break;
|
||||
case "verification":
|
||||
xml.Read();
|
||||
Verification = Convert.FromBase64String(xml.Value.Trim());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
xml.Close();
|
||||
//if failed, return false
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Save(IOConnectionInfo ioc)
|
||||
{
|
||||
Stream sOut = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (var trans = App.Kp2a.GetOtpAuxFileStorage(ioc)
|
||||
.OpenWriteTransaction(ioc, App.Kp2a.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
|
||||
{
|
||||
var stream = trans.OpenFile();
|
||||
if (SaveStream(sOut))
|
||||
{
|
||||
trans.CommitWrite();
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { return false; }
|
||||
finally
|
||||
{
|
||||
if(sOut != null) sOut.Close();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool SaveStream(Stream file)
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlWriterSettings settings = new XmlWriterSettings();
|
||||
settings.CloseOutput = true;
|
||||
settings.Indent = true;
|
||||
settings.IndentChars = "\t";
|
||||
settings.NewLineOnAttributes = true;
|
||||
|
||||
XmlWriter xml = XmlWriter.Create(file,settings);
|
||||
xml.WriteStartDocument();
|
||||
xml.WriteStartElement("data");
|
||||
|
||||
xml.WriteStartElement("aes");
|
||||
xml.WriteElementString("encrypted", Convert.ToBase64String(EncryptedSecret));
|
||||
xml.WriteElementString("iv", Convert.ToBase64String(IV));
|
||||
xml.WriteEndElement();
|
||||
|
||||
xml.WriteElementString("challenge", Convert.ToBase64String(Challenge));
|
||||
xml.WriteElementString("verification", Convert.ToBase64String(Verification));
|
||||
|
||||
xml.WriteEndElement();
|
||||
xml.WriteEndDocument();
|
||||
xml.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
196
src/keepass2android/KeeChallenge.cs
Normal file
196
src/keepass2android/KeeChallenge.cs
Normal file
@ -0,0 +1,196 @@
|
||||
/* KeeChallenge--Provides Yubikey challenge-response capability to Keepass
|
||||
* Copyright (C) 2014 Ben Rush
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Utility;
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Serialization;
|
||||
|
||||
using keepass2android;
|
||||
using keepass2android.Io;
|
||||
|
||||
namespace KeeChallenge
|
||||
{
|
||||
public sealed class KeeChallengeProv
|
||||
{
|
||||
private const string m_name = "Yubikey challenge-response";
|
||||
|
||||
public static string Name { get { return m_name; } }
|
||||
|
||||
public const int keyLenBytes = 20;
|
||||
public const int challengeLenBytes = 64;
|
||||
public const int responseLenBytes = 20;
|
||||
public const int secretLenBytes = 20;
|
||||
|
||||
private KeeChallengeProv()
|
||||
{
|
||||
}
|
||||
|
||||
private static byte[] GenerateChallenge()
|
||||
{
|
||||
CryptoRandom rand = CryptoRandom.Instance;
|
||||
return CryptoRandom.Instance.GetRandomBytes(challengeLenBytes);
|
||||
}
|
||||
|
||||
private static byte[] GenerateResponse(byte[] challenge, byte[] key)
|
||||
{
|
||||
HMACSHA1 hmac = new HMACSHA1(key);
|
||||
byte[] resp = hmac.ComputeHash(challenge);
|
||||
hmac.Clear();
|
||||
return resp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method for generating encrypted ChallengeInfo to be saved. For security, this method should
|
||||
/// be called every time you get a successful challenge-response pair from the Yubikey. Failure to
|
||||
/// do so will permit password re-use attacks.
|
||||
/// </summary>
|
||||
/// <param name="secret">The un-encrypted secret</param>
|
||||
/// <returns>A fully populated ChallengeInfo object ready to be saved</returns>
|
||||
public static ChallengeInfo Encrypt(byte[] secret)
|
||||
{
|
||||
//generate a random challenge for use next time
|
||||
byte[] challenge = GenerateChallenge();
|
||||
|
||||
//generate the expected HMAC-SHA1 response for the challenge based on the secret
|
||||
byte[] resp = GenerateResponse(challenge, secret);
|
||||
|
||||
//use the response to encrypt the secret
|
||||
SHA256 sha = SHA256Managed.Create();
|
||||
byte[] key = sha.ComputeHash(resp); // get a 256 bit key from the 160 bit hmac response
|
||||
byte[] secretHash = sha.ComputeHash(secret);
|
||||
|
||||
AesManaged aes = new AesManaged();
|
||||
aes.KeySize = key.Length * sizeof(byte) * 8; //pedantic, but foolproof
|
||||
aes.Key = key;
|
||||
aes.GenerateIV();
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
byte[] iv = aes.IV;
|
||||
|
||||
byte[] encrypted;
|
||||
ICryptoTransform enc = aes.CreateEncryptor();
|
||||
using (MemoryStream msEncrypt = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, enc, CryptoStreamMode.Write))
|
||||
{
|
||||
csEncrypt.Write(secret, 0, secret.Length);
|
||||
csEncrypt.FlushFinalBlock();
|
||||
|
||||
encrypted = msEncrypt.ToArray();
|
||||
csEncrypt.Close();
|
||||
csEncrypt.Clear();
|
||||
}
|
||||
msEncrypt.Close();
|
||||
}
|
||||
|
||||
ChallengeInfo inf = new ChallengeInfo (encrypted, aes.IV, challenge, secretHash);
|
||||
|
||||
sha.Clear();
|
||||
aes.Clear();
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
private static bool DecryptSecret(byte[] yubiResp, ChallengeInfo inf, out byte[] secret)
|
||||
{
|
||||
secret = new byte[keyLenBytes];
|
||||
|
||||
if (inf.IV == null) return false;
|
||||
if (inf.Verification == null) return false;
|
||||
|
||||
//use the response to decrypt the secret
|
||||
SHA256 sha = SHA256Managed.Create();
|
||||
byte[] key = sha.ComputeHash(yubiResp); // get a 256 bit key from the 160 bit hmac response
|
||||
|
||||
AesManaged aes = new AesManaged();
|
||||
aes.KeySize = key.Length * sizeof(byte) * 8; //pedantic, but foolproof
|
||||
aes.Key = key;
|
||||
aes.IV = inf.IV;
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
|
||||
|
||||
ICryptoTransform dec = aes.CreateDecryptor();
|
||||
using (MemoryStream msDecrypt = new MemoryStream(inf.EncryptedSecret))
|
||||
{
|
||||
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, dec, CryptoStreamMode.Read))
|
||||
{
|
||||
csDecrypt.Read(secret, 0, secret.Length);
|
||||
csDecrypt.Close();
|
||||
csDecrypt.Clear();
|
||||
}
|
||||
msDecrypt.Close();
|
||||
}
|
||||
|
||||
byte[] secretHash = sha.ComputeHash(secret);
|
||||
for (int i = 0; i < secretHash.Length; i++)
|
||||
{
|
||||
if (secretHash[i] != inf.Verification[i])
|
||||
{
|
||||
//wrong response
|
||||
Array.Clear(secret, 0, secret.Length);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//return the secret
|
||||
sha.Clear();
|
||||
aes.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The primary access point for challenge-response utility functions. Accepts a pre-populated ChallengeInfo object
|
||||
/// containing at least the IV, EncryptedSecret, and Verification fields. These fields are combined with the Yubikey response
|
||||
/// to decrypt and verify the secret.
|
||||
/// </summary>
|
||||
/// <param name="inf">A pre-populated object containing minimally the IV, EncryptedSecret and Verification fields.
|
||||
/// This should be populated from the database.xml auxilliary file</param>
|
||||
/// <param name="resp">The Yubikey's response to the issued challenge</param>
|
||||
/// <returns>The common secret, used as a composite key to encrypt a Keepass database</returns>
|
||||
public static byte[] GetSecret(ChallengeInfo inf, byte[] resp)
|
||||
{
|
||||
if (resp.Length != responseLenBytes)
|
||||
return null;
|
||||
if (inf == null)
|
||||
return null;
|
||||
if (inf.Challenge == null ||
|
||||
inf.Verification == null)
|
||||
return null;
|
||||
|
||||
byte[] secret;
|
||||
|
||||
if (DecryptSecret(resp, inf, out secret))
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ using MemoryStream = System.IO.MemoryStream;
|
||||
using Object = Java.Lang.Object;
|
||||
using Process = Android.OS.Process;
|
||||
using String = System.String;
|
||||
using KeeChallenge;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@ -60,7 +61,9 @@ namespace keepass2android
|
||||
None = 0,
|
||||
KeyFile = 1,
|
||||
Otp = 2,
|
||||
OtpRecovery = 3
|
||||
OtpRecovery = 3,
|
||||
Chal = 4,
|
||||
ChalRecovery = 5
|
||||
}
|
||||
|
||||
public const String KeyDefaultFilename = "defaultFileName";
|
||||
@ -76,9 +79,12 @@ namespace keepass2android
|
||||
private const string ShowpasswordKey = "ShowPassword";
|
||||
private const string KeyProviderIdOtp = "KP2A-OTP";
|
||||
private const string KeyProviderIdOtpRecovery = "KP2A-OTPSecret";
|
||||
private const string KeyProviderIdChallenge = "KP2A-Chal";
|
||||
private const string KeyProviderIdChallengeRecovery = "KP2A-ChalSecret";
|
||||
|
||||
private const int RequestCodePrepareDbFile = 1000;
|
||||
private const int RequestCodePrepareOtpAuxFile = 1001;
|
||||
private const int RequestCodeChallengeYubikey = 1002;
|
||||
|
||||
|
||||
private Task<MemoryStream> _loadDbTask;
|
||||
@ -104,6 +110,10 @@ namespace keepass2android
|
||||
return KeyProviders.Otp;
|
||||
if (_keyFileOrProvider == KeyProviderIdOtpRecovery)
|
||||
return KeyProviders.OtpRecovery;
|
||||
if (_keyFileOrProvider == KeyProviderIdChallenge)
|
||||
return KeyProviders.Chal;
|
||||
if (_keyFileOrProvider == KeyProviderIdChallengeRecovery)
|
||||
return KeyProviders.ChalRecovery;
|
||||
return KeyProviders.KeyFile;
|
||||
}
|
||||
}
|
||||
@ -113,6 +123,8 @@ namespace keepass2android
|
||||
|
||||
private bool _starting;
|
||||
private OtpInfo _otpInfo;
|
||||
private ChallengeInfo _chalInfo;
|
||||
private byte[] _challengeSecret;
|
||||
private readonly int[] _otpTextViewIds = new[] {Resource.Id.otp1, Resource.Id.otp2, Resource.Id.otp3, Resource.Id.otp4, Resource.Id.otp5, Resource.Id.otp6};
|
||||
private const string OtpInfoKey = "OtpInfoKey";
|
||||
private const string EnteredOtpsKey = "EnteredOtpsKey";
|
||||
@ -120,6 +132,7 @@ namespace keepass2android
|
||||
private const string PasswordKey = "PasswordKey";
|
||||
private const string KeyFileOrProviderKey = "KeyFileOrProviderKey";
|
||||
|
||||
|
||||
private ActivityDesign _design;
|
||||
private bool _performingLoad;
|
||||
|
||||
@ -270,10 +283,60 @@ namespace keepass2android
|
||||
if (requestCode == RequestCodePrepareDbFile)
|
||||
PerformLoadDatabase();
|
||||
if (requestCode == RequestCodePrepareOtpAuxFile)
|
||||
LoadOtpFile();
|
||||
break;
|
||||
{
|
||||
if (_keyFileOrProvider == KeyProviderIdChallenge)
|
||||
{
|
||||
LoadChalFile();
|
||||
|
||||
} else {
|
||||
LoadOtpFile ();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (requestCode == RequestCodeChallengeYubikey && resultCode == Result.Ok)
|
||||
{
|
||||
byte[] challengeResponse = data.GetByteArrayExtra("response");
|
||||
_challengeSecret = KeeChallengeProv.GetSecret(_chalInfo, challengeResponse);
|
||||
UpdateOkButtonState();
|
||||
if (_challengeSecret != null)
|
||||
{
|
||||
new LoadingDialog<object, object, object>(this, true,
|
||||
//doInBackground
|
||||
delegate
|
||||
{
|
||||
//save aux file
|
||||
try
|
||||
{
|
||||
ChallengeInfo temp = KeeChallengeProv.Encrypt(_challengeSecret);
|
||||
IFileStorage fileStorage = App.Kp2a.GetOtpAuxFileStorage(_ioConnection);
|
||||
IOConnectionInfo iocAux = fileStorage.GetFilePath(fileStorage.GetParentPath(_ioConnection),
|
||||
fileStorage.GetFilenameWithoutPathAndExt(_ioConnection) + ".xml");
|
||||
if (!temp.Save(iocAux))
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.ErrorUpdatingChalAuxFile, ToastLength.Long).Show();
|
||||
return false;
|
||||
}
|
||||
Array.Clear(challengeResponse, 0, challengeResponse.Length);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log(e.ToString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
, delegate
|
||||
{
|
||||
|
||||
}).Execute();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.bad_resp, ToastLength.Long).Show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -313,6 +376,61 @@ namespace keepass2android
|
||||
).Execute();
|
||||
}
|
||||
|
||||
private void LoadChalFile()
|
||||
{
|
||||
new LoadingDialog<object, object, object>(this, true,
|
||||
//doInBackground
|
||||
delegate
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
IFileStorage fileStorage =
|
||||
App.Kp2a.GetOtpAuxFileStorage(_ioConnection);
|
||||
IOConnectionInfo iocAux =
|
||||
fileStorage.GetFilePath(
|
||||
fileStorage.GetParentPath(_ioConnection),
|
||||
fileStorage.GetFilenameWithoutPathAndExt(_ioConnection) +
|
||||
".xml");
|
||||
|
||||
_chalInfo = ChallengeInfo.Load(iocAux);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log(e.ToString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
, delegate
|
||||
{
|
||||
if (_chalInfo == null)
|
||||
{
|
||||
Toast.MakeText(this,
|
||||
GetString(Resource.String.CouldntLoadChalAuxFile) +
|
||||
" " +
|
||||
GetString(
|
||||
Resource.String.CouldntLoadChalAuxFile_Hint)
|
||||
, ToastLength.Long).Show();
|
||||
return;
|
||||
|
||||
}
|
||||
Intent chalIntent = new Intent("com.yubichallenge.NFCActivity.CHALLENGE");
|
||||
chalIntent.PutExtra("challenge", _chalInfo.Challenge);
|
||||
chalIntent.PutExtra("slot", 2);
|
||||
IList<ResolveInfo> activities = PackageManager.QueryIntentActivities(chalIntent, 0);
|
||||
bool isIntentSafe = activities.Count > 0;
|
||||
if (isIntentSafe)
|
||||
{
|
||||
StartActivityForResult(chalIntent, RequestCodeChallengeYubikey);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO message no plugin!
|
||||
}
|
||||
}).Execute();
|
||||
|
||||
}
|
||||
|
||||
private void ShowOtpEntry(IList<string> prefilledOtps)
|
||||
{
|
||||
FindViewById(Resource.Id.otpInitView).Visibility = ViewStates.Gone;
|
||||
@ -608,6 +726,12 @@ namespace keepass2android
|
||||
case 3:
|
||||
_keyFileOrProvider = KeyProviderIdOtpRecovery;
|
||||
break;
|
||||
case 4:
|
||||
_keyFileOrProvider = KeyProviderIdChallenge;
|
||||
break;
|
||||
case 5:
|
||||
_keyFileOrProvider = KeyProviderIdChallengeRecovery;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unexpected position " + args.Position + " / " +
|
||||
((ICursor) ((AdapterView) sender).GetItemAtPosition(args.Position)).GetString(1));
|
||||
@ -616,9 +740,9 @@ namespace keepass2android
|
||||
};
|
||||
FindViewById(Resource.Id.init_otp).Click += (sender, args) =>
|
||||
{
|
||||
App.Kp2a.GetOtpAuxFileStorage(_ioConnection)
|
||||
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), _ioConnection,
|
||||
RequestCodePrepareOtpAuxFile, false);
|
||||
App.Kp2a.GetOtpAuxFileStorage(_ioConnection)
|
||||
.PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), _ioConnection,
|
||||
RequestCodePrepareOtpAuxFile, false);
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -692,6 +816,12 @@ namespace keepass2android
|
||||
case KeyProviders.OtpRecovery:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = FindViewById<EditText>(Resource.Id.pass_otpsecret).Text != "" && _password != "";
|
||||
break;
|
||||
case KeyProviders.Chal:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = _challengeSecret != null;
|
||||
break;
|
||||
case KeyProviders.ChalRecovery:
|
||||
FindViewById(Resource.Id.pass_ok).Enabled = true;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
@ -713,6 +843,12 @@ namespace keepass2android
|
||||
{
|
||||
FindViewById(Resource.Id.otps_pending).Visibility = _pendingOtps.Count > 0 ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
if (KeyProviderType == KeyProviders.Chal)
|
||||
{
|
||||
FindViewById (Resource.Id.otpView).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.otps_pending).Visibility = ViewStates.Gone;
|
||||
}
|
||||
UpdateOkButtonState();
|
||||
}
|
||||
|
||||
@ -768,6 +904,11 @@ namespace keepass2android
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (KeyProviderType == KeyProviders.Chal)
|
||||
{
|
||||
compositeKey.AddUserKey(new KcpCustomKey(KeeChallengeProv.Name, _challengeSecret, true));
|
||||
|
||||
}
|
||||
|
||||
CheckBox cbQuickUnlock = (CheckBox) FindViewById(Resource.Id.enable_quickunlock);
|
||||
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
|
||||
@ -1136,12 +1277,8 @@ namespace keepass2android
|
||||
{
|
||||
if ( Success )
|
||||
{
|
||||
_act.SetEditText(Resource.Id.password, "");
|
||||
_act.SetEditText(Resource.Id.pass_otpsecret, "");
|
||||
foreach (int otpId in _act._otpTextViewIds)
|
||||
{
|
||||
_act.SetEditText(otpId, "");
|
||||
}
|
||||
|
||||
_act.ClearEnteredPassword();
|
||||
|
||||
_act.LaunchNextActivity();
|
||||
|
||||
@ -1155,6 +1292,18 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearEnteredPassword()
|
||||
{
|
||||
SetEditText(Resource.Id.password, "");
|
||||
SetEditText(Resource.Id.pass_otpsecret, "");
|
||||
foreach (int otpId in _otpTextViewIds)
|
||||
{
|
||||
SetEditText(otpId, "");
|
||||
}
|
||||
Array.Clear(_challengeSecret, 0, _challengeSecret.Length);
|
||||
_challengeSecret = null;
|
||||
}
|
||||
|
||||
class SaveOtpAuxFileAndLoadDb : LoadDb
|
||||
{
|
||||
private readonly PasswordActivity _act;
|
||||
|
@ -512,6 +512,8 @@ Erstes öffentliches Release</string>
|
||||
<item>Kennwort + Schlüsseldatei</item>
|
||||
<item>Kennwort + OTP</item>
|
||||
<item>Kennwort + OTP Secret (Recovery-Modus)</item>
|
||||
<item>Password + Challenge-Response</item>
|
||||
<item>Password + Challenge-Response secret (recovery mode)</item>
|
||||
</string-array>
|
||||
<string-array name="AcceptAllServerCertificates_options">
|
||||
<item>Fehler bei Zertifikatsvalidierung ignorieren</item>
|
||||
|
@ -395,7 +395,7 @@
|
||||
|
||||
<string name="error_adding_keyfile">Error while adding the key file!</string>
|
||||
|
||||
<string name="init_otp">Load OTP auxiliary file…</string>
|
||||
<string name="init_otp">Load auxiliary file…</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>
|
||||
@ -410,6 +410,11 @@
|
||||
<string name="ErrorUpdatingOtpAuxFile">Error updating OTP auxiliary file!</string>
|
||||
<string name="SavingOtpAuxFile">Saving auxiliary OTP file…</string>
|
||||
|
||||
<string name="bad_resp">The challenge response is incorrect.</string>
|
||||
<string name="CouldntLoadChalAuxFile">Could not load auxiliary challenge file!</string>
|
||||
<string name="CouldntLoadChalAuxFile_Hint">Please use the KeeChallenge plugin in KeePass 2.x (PC) to configure your database for use with challenge-response!</string>
|
||||
<string name="ErrorUpdatingChalAuxFile">Error updating OTP auxiliary file!</string>
|
||||
|
||||
<string name="loading">Loading…</string>
|
||||
|
||||
<string name="plugins">Plug-ins</string>
|
||||
@ -601,6 +606,8 @@ Initial public release
|
||||
<item>Password + Key file</item>
|
||||
<item>Password + OTP</item>
|
||||
<item>Password + OTP secret (recovery mode)</item>
|
||||
<item>Password + Challenge-Response</item>
|
||||
<item>Password + Challenge-Response secret (recovery mode)</item>
|
||||
</string-array>
|
||||
<string-array name="AcceptAllServerCertificates_options">
|
||||
<item>Ignore certificate validation failures</item>
|
||||
|
@ -98,6 +98,7 @@
|
||||
<Compile Include="app\NoFileStorageFoundException.cs" />
|
||||
<Compile Include="app\OtpAuxCacheSupervisor.cs" />
|
||||
<Compile Include="AboutActivity.cs" />
|
||||
<Compile Include="ChallengeInfo.cs" />
|
||||
<Compile Include="CreateDatabaseActivity.cs" />
|
||||
<Compile Include="CreateNewFilename.cs" />
|
||||
<Compile Include="EntryActivityClasses\CopyToClipboardPopupMenuIcon.cs" />
|
||||
@ -121,6 +122,7 @@
|
||||
<Compile Include="ChangeLog.cs" />
|
||||
<Compile Include="icons\DrawableFactory.cs" />
|
||||
<Compile Include="icons\Icons.cs" />
|
||||
<Compile Include="KeeChallenge.cs" />
|
||||
<Compile Include="NfcOtpActivity.cs" />
|
||||
<Compile Include="pluginhost\PluginArrayAdapter.cs" />
|
||||
<Compile Include="pluginhost\PluginDatabase.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user