-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:
brush701 2015-02-09 12:56:05 -08:00
parent 8779c90da9
commit f91a15e03c
3 changed files with 56 additions and 16 deletions

View File

@ -31,6 +31,8 @@ namespace KeeChallenge
{
public class ChallengeInfo
{
private bool m_LT64;
public byte[] EncryptedSecret {
get;
private set;
@ -51,16 +53,24 @@ namespace KeeChallenge
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;
IV = iv;
Challenge = challenge;
Verification = verification;
LT64 = lt64;
}
public static ChallengeInfo Load(IOConnectionInfo ioc)
@ -125,6 +135,10 @@ namespace KeeChallenge
xml.Read();
Verification = Convert.FromBase64String(xml.Value.Trim());
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("verification", Convert.ToBase64String(Verification));
xml.WriteElementString("lt64", LT64.ToString());
xml.WriteEndElement();
xml.WriteEndDocument();

View File

@ -46,19 +46,40 @@ namespace KeeChallenge
public const int responseLenBytes = 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
{
}
private static byte[] GenerateChallenge()
{
CryptoRandom rand = CryptoRandom.Instance;
return CryptoRandom.Instance.GetRandomBytes(challengeLenBytes);
get;
set;
}
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);
if (LT64)
challenge = challenge.Take(challengeLenBytes - 1).ToArray();
byte[] resp = hmac.ComputeHash(challenge);
hmac.Clear();
return resp;
@ -71,7 +92,7 @@ namespace KeeChallenge
/// </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)
public ChallengeInfo Encrypt(byte[] secret)
{
//generate a random challenge for use next time
byte[] challenge = GenerateChallenge();
@ -101,14 +122,14 @@ namespace KeeChallenge
msEncrypt.Close();
}
ChallengeInfo inf = new ChallengeInfo (encrypted, IV, challenge, secretHash);
ChallengeInfo inf = new ChallengeInfo (encrypted, IV, challenge, secretHash, LT64);
sha.Clear();
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];
@ -156,7 +177,7 @@ namespace KeeChallenge
/// 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)
public byte[] GetSecret(ChallengeInfo inf, byte[] resp)
{
if (resp.Length != responseLenBytes)
return null;
@ -165,6 +186,8 @@ namespace KeeChallenge
if (inf.Challenge == null ||
inf.Verification == null)
return null;
LT64 = inf.LT64;
byte[] secret;

View File

@ -129,6 +129,7 @@ namespace keepass2android
private OtpInfo _otpInfo;
private ChallengeInfo _chalInfo;
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 const string OtpInfoKey = "OtpInfoKey";
private const string EnteredOtpsKey = "EnteredOtpsKey";
@ -325,8 +326,9 @@ namespace keepass2android
{
try
{
_challengeProv = new KeeChallengeProv();
byte[] challengeResponse = data.GetByteArrayExtra("response");
_challengeSecret = KeeChallengeProv.GetSecret(_chalInfo, challengeResponse);
_challengeSecret = _challengeProv.GetSecret(_chalInfo, challengeResponse);
Array.Clear(challengeResponse, 0, challengeResponse.Length);
}
catch (Exception e)
@ -348,7 +350,7 @@ namespace keepass2android
//save aux file
try
{
ChallengeInfo temp = KeeChallengeProv.Encrypt(_challengeSecret);
ChallengeInfo temp = _challengeProv.Encrypt(_challengeSecret);
IFileStorage fileStorage = App.Kp2a.GetOtpAuxFileStorage(_ioConnection);
IOConnectionInfo iocAux = fileStorage.GetFilePath(fileStorage.GetParentPath(_ioConnection),
fileStorage.GetFilenameWithoutPathAndExt(_ioConnection) + ".xml");