-Added support for variable length challenges
-New "lt64" field in xml file handled properly -KeeChallengeProv is no longer a static class but object oriented
This commit is contained in:
@@ -31,6 +31,8 @@ namespace KeeChallenge
|
|||||||
{
|
{
|
||||||
public class ChallengeInfo
|
public class ChallengeInfo
|
||||||
{
|
{
|
||||||
|
private bool m_LT64;
|
||||||
|
|
||||||
public byte[] EncryptedSecret {
|
public byte[] EncryptedSecret {
|
||||||
get;
|
get;
|
||||||
private set;
|
private set;
|
||||||
@@ -51,16 +53,24 @@ namespace KeeChallenge
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChallengeInfo()
|
public bool LT64
|
||||||
{
|
{
|
||||||
|
get { return m_LT64; }
|
||||||
|
private set { m_LT64 = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChallengeInfo(byte[] encryptedSecret, byte[] iv, byte[] challenge, byte[] verification)
|
private ChallengeInfo()
|
||||||
|
{
|
||||||
|
LT64 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChallengeInfo(byte[] encryptedSecret, byte[] iv, byte[] challenge, byte[] verification, bool lt64)
|
||||||
{
|
{
|
||||||
EncryptedSecret = encryptedSecret;
|
EncryptedSecret = encryptedSecret;
|
||||||
IV = iv;
|
IV = iv;
|
||||||
Challenge = challenge;
|
Challenge = challenge;
|
||||||
Verification = verification;
|
Verification = verification;
|
||||||
|
LT64 = lt64;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChallengeInfo Load(IOConnectionInfo ioc)
|
public static ChallengeInfo Load(IOConnectionInfo ioc)
|
||||||
@@ -125,6 +135,10 @@ namespace KeeChallenge
|
|||||||
xml.Read();
|
xml.Read();
|
||||||
Verification = Convert.FromBase64String(xml.Value.Trim());
|
Verification = Convert.FromBase64String(xml.Value.Trim());
|
||||||
break;
|
break;
|
||||||
|
case "lt64":
|
||||||
|
xml.Read();
|
||||||
|
if (!bool.TryParse(xml.Value.Trim(), out m_LT64)) throw new Exception("Unable to parse LT64 flag");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +198,7 @@ namespace KeeChallenge
|
|||||||
|
|
||||||
xml.WriteElementString("challenge", Convert.ToBase64String(Challenge));
|
xml.WriteElementString("challenge", Convert.ToBase64String(Challenge));
|
||||||
xml.WriteElementString("verification", Convert.ToBase64String(Verification));
|
xml.WriteElementString("verification", Convert.ToBase64String(Verification));
|
||||||
|
xml.WriteElementString("lt64", LT64.ToString());
|
||||||
|
|
||||||
xml.WriteEndElement();
|
xml.WriteEndElement();
|
||||||
xml.WriteEndDocument();
|
xml.WriteEndDocument();
|
||||||
|
|||||||
@@ -46,19 +46,40 @@ namespace KeeChallenge
|
|||||||
public const int responseLenBytes = 20;
|
public const int responseLenBytes = 20;
|
||||||
public const int secretLenBytes = 20;
|
public const int secretLenBytes = 20;
|
||||||
|
|
||||||
private KeeChallengeProv()
|
//If variable length challenges are enabled, a 63 byte challenge is sent instead.
|
||||||
|
//See GenerateChallenge() and http://forum.yubico.com/viewtopic.php?f=16&t=1078
|
||||||
|
//This field is automatically set by calling GetSecret(). However, when creating
|
||||||
|
//a new database it will need to be set manually based on the user's yubikey settings
|
||||||
|
public bool LT64
|
||||||
{
|
{
|
||||||
}
|
get;
|
||||||
|
set;
|
||||||
private static byte[] GenerateChallenge()
|
|
||||||
{
|
|
||||||
CryptoRandom rand = CryptoRandom.Instance;
|
|
||||||
return CryptoRandom.Instance.GetRandomBytes(challengeLenBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] GenerateResponse(byte[] challenge, byte[] key)
|
private KeeChallengeProv()
|
||||||
|
{
|
||||||
|
LT64 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] GenerateChallenge()
|
||||||
|
{
|
||||||
|
CryptoRandom rand = CryptoRandom.Instance;
|
||||||
|
byte[] chal = CryptoRandom.Instance.GetRandomBytes(challengeLenBytes);
|
||||||
|
if (LT64)
|
||||||
|
{
|
||||||
|
chal[challengeLenBytes - 2] = (byte)~chal[challengeLenBytes - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return chal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] GenerateResponse(byte[] challenge, byte[] key)
|
||||||
{
|
{
|
||||||
HMACSHA1 hmac = new HMACSHA1(key);
|
HMACSHA1 hmac = new HMACSHA1(key);
|
||||||
|
|
||||||
|
if (LT64)
|
||||||
|
challenge = challenge.Take(challengeLenBytes - 1).ToArray();
|
||||||
|
|
||||||
byte[] resp = hmac.ComputeHash(challenge);
|
byte[] resp = hmac.ComputeHash(challenge);
|
||||||
hmac.Clear();
|
hmac.Clear();
|
||||||
return resp;
|
return resp;
|
||||||
@@ -71,7 +92,7 @@ namespace KeeChallenge
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="secret">The un-encrypted secret</param>
|
/// <param name="secret">The un-encrypted secret</param>
|
||||||
/// <returns>A fully populated ChallengeInfo object ready to be saved</returns>
|
/// <returns>A fully populated ChallengeInfo object ready to be saved</returns>
|
||||||
public static ChallengeInfo Encrypt(byte[] secret)
|
public ChallengeInfo Encrypt(byte[] secret)
|
||||||
{
|
{
|
||||||
//generate a random challenge for use next time
|
//generate a random challenge for use next time
|
||||||
byte[] challenge = GenerateChallenge();
|
byte[] challenge = GenerateChallenge();
|
||||||
@@ -101,14 +122,14 @@ namespace KeeChallenge
|
|||||||
msEncrypt.Close();
|
msEncrypt.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
ChallengeInfo inf = new ChallengeInfo (encrypted, IV, challenge, secretHash);
|
ChallengeInfo inf = new ChallengeInfo (encrypted, IV, challenge, secretHash, LT64);
|
||||||
|
|
||||||
sha.Clear();
|
sha.Clear();
|
||||||
|
|
||||||
return inf;
|
return inf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool DecryptSecret(byte[] yubiResp, ChallengeInfo inf, out byte[] secret)
|
private bool DecryptSecret(byte[] yubiResp, ChallengeInfo inf, out byte[] secret)
|
||||||
{
|
{
|
||||||
secret = new byte[keyLenBytes];
|
secret = new byte[keyLenBytes];
|
||||||
|
|
||||||
@@ -156,7 +177,7 @@ namespace KeeChallenge
|
|||||||
/// This should be populated from the database.xml auxilliary file</param>
|
/// This should be populated from the database.xml auxilliary file</param>
|
||||||
/// <param name="resp" >The Yubikey's response to the issued challenge</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>
|
/// <returns>The common secret, used as a composite key to encrypt a Keepass database</returns>
|
||||||
public static byte[] GetSecret(ChallengeInfo inf, byte[] resp)
|
public byte[] GetSecret(ChallengeInfo inf, byte[] resp)
|
||||||
{
|
{
|
||||||
if (resp.Length != responseLenBytes)
|
if (resp.Length != responseLenBytes)
|
||||||
return null;
|
return null;
|
||||||
@@ -165,6 +186,8 @@ namespace KeeChallenge
|
|||||||
if (inf.Challenge == null ||
|
if (inf.Challenge == null ||
|
||||||
inf.Verification == null)
|
inf.Verification == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
LT64 = inf.LT64;
|
||||||
|
|
||||||
byte[] secret;
|
byte[] secret;
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ namespace keepass2android
|
|||||||
private OtpInfo _otpInfo;
|
private OtpInfo _otpInfo;
|
||||||
private ChallengeInfo _chalInfo;
|
private ChallengeInfo _chalInfo;
|
||||||
private byte[] _challengeSecret;
|
private byte[] _challengeSecret;
|
||||||
|
private KeeChallengeProv _challengeProv;
|
||||||
private readonly int[] _otpTextViewIds = new[] {Resource.Id.otp1, Resource.Id.otp2, Resource.Id.otp3, Resource.Id.otp4, Resource.Id.otp5, Resource.Id.otp6};
|
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 OtpInfoKey = "OtpInfoKey";
|
||||||
private const string EnteredOtpsKey = "EnteredOtpsKey";
|
private const string EnteredOtpsKey = "EnteredOtpsKey";
|
||||||
@@ -325,8 +326,9 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_challengeProv = new KeeChallengeProv();
|
||||||
byte[] challengeResponse = data.GetByteArrayExtra("response");
|
byte[] challengeResponse = data.GetByteArrayExtra("response");
|
||||||
_challengeSecret = KeeChallengeProv.GetSecret(_chalInfo, challengeResponse);
|
_challengeSecret = _challengeProv.GetSecret(_chalInfo, challengeResponse);
|
||||||
Array.Clear(challengeResponse, 0, challengeResponse.Length);
|
Array.Clear(challengeResponse, 0, challengeResponse.Length);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -348,7 +350,7 @@ namespace keepass2android
|
|||||||
//save aux file
|
//save aux file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ChallengeInfo temp = KeeChallengeProv.Encrypt(_challengeSecret);
|
ChallengeInfo temp = _challengeProv.Encrypt(_challengeSecret);
|
||||||
IFileStorage fileStorage = App.Kp2a.GetOtpAuxFileStorage(_ioConnection);
|
IFileStorage fileStorage = App.Kp2a.GetOtpAuxFileStorage(_ioConnection);
|
||||||
IOConnectionInfo iocAux = fileStorage.GetFilePath(fileStorage.GetParentPath(_ioConnection),
|
IOConnectionInfo iocAux = fileStorage.GetFilePath(fileStorage.GetParentPath(_ioConnection),
|
||||||
fileStorage.GetFilenameWithoutPathAndExt(_ioConnection) + ".xml");
|
fileStorage.GetFilenameWithoutPathAndExt(_ioConnection) + ".xml");
|
||||||
|
|||||||
Reference in New Issue
Block a user