Skip to content

Commit

Permalink
Merge pull request #150 from RomanSoloweow/master
Browse files Browse the repository at this point in the history
Support ValidateTwoFactorPIN with iterationOffset as parameter
  • Loading branch information
ahwm authored May 1, 2023
2 parents 57e50d1 + f5af0e7 commit 117ec8f
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
3 changes: 3 additions & 0 deletions Google.Authenticator.Tests/ValidationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public void ValidateWorksWithDifferentSecretTypes(string pin, int irrelevantNumb
subject.ValidateTwoFactorPIN(secretAsBytes, pin, TimeSpan.FromMinutes(irrelevantNumberToAvoidDuplicatePinsBeingRemoved));
subject.ValidateTwoFactorPIN(secretAsBase32, pin, true);
subject.ValidateTwoFactorPIN(secretAsBase32, pin, TimeSpan.FromMinutes(irrelevantNumberToAvoidDuplicatePinsBeingRemoved), true);
subject.ValidateTwoFactorPIN(secret, pin, irrelevantNumberToAvoidDuplicatePinsBeingRemoved * 2, false);
subject.ValidateTwoFactorPIN(secretAsBase32, pin, irrelevantNumberToAvoidDuplicatePinsBeingRemoved * 2, true);
subject.ValidateTwoFactorPIN(secretAsBytes, pin, irrelevantNumberToAvoidDuplicatePinsBeingRemoved * 2);
}

public static IEnumerable<object[]> GetPins()
Expand Down
45 changes: 37 additions & 8 deletions Google.Authenticator/TwoFactorAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,30 @@ public bool ValidateTwoFactorPIN(byte[] accountSecretKey, string twoFactorCodeFr
/// <param name="twoFactorCodeFromClient">The PIN from the client</param>
/// <param name="timeTolerance">The time window within which to check to allow for clock drift between devices.</param>
/// <returns>True if PIN is currently valid</returns>
public bool ValidateTwoFactorPIN(byte[] accountSecretKey, string twoFactorCodeFromClient, TimeSpan timeTolerance)
{
return GetCurrentPINs(accountSecretKey, timeTolerance).Any(c => c == twoFactorCodeFromClient);
}
public bool ValidateTwoFactorPIN(byte[] accountSecretKey, string twoFactorCodeFromClient, TimeSpan timeTolerance) =>
GetCurrentPINs(accountSecretKey, timeTolerance).Any(c => c == twoFactorCodeFromClient);

/// <summary>
/// Given a PIN from a client, check if it is valid at the current time.
/// </summary>
/// <param name="accountSecretKey">Account Secret Key</param>
/// <param name="twoFactorCodeFromClient">The PIN from the client</param>
/// <param name="iterationOffset">The counter window within which to check to allow for clock drift between devices.</param>
/// <param name="secretIsBase32">Flag saying if accountSecretKey is in Base32 format or original secret</param>
/// <returns>True if PIN is currently valid</returns>
public bool ValidateTwoFactorPIN(string accountSecretKey, string twoFactorCodeFromClient, int iterationOffset, bool secretIsBase32 = false) =>
ValidateTwoFactorPIN(ConvertSecretToBytes(accountSecretKey, secretIsBase32), twoFactorCodeFromClient, iterationOffset);

/// <summary>
/// Given a PIN from a client, check if it is valid at the current time.
/// </summary>
/// <param name="accountSecretKey">Account Secret Key</param>
/// <param name="twoFactorCodeFromClient">The PIN from the client</param>
/// <param name="iterationOffset">The counter window within which to check to allow for clock drift between devices.</param>
/// <returns>True if PIN is currently valid</returns>
public bool ValidateTwoFactorPIN(byte[] accountSecretKey, string twoFactorCodeFromClient, int iterationOffset) =>
GetCurrentPINs(accountSecretKey, iterationOffset).Any(c => c == twoFactorCodeFromClient);

/// <summary>
/// Get the PIN for current time; the same code that a 2FA app would generate for the current time.
/// Do not validate directly against this as clockdrift may cause a a different PIN to be generated than one you did a second ago.
Expand Down Expand Up @@ -282,15 +301,25 @@ public string[] GetCurrentPINs(byte[] accountSecretKey) =>
/// <returns></returns>
public string[] GetCurrentPINs(byte[] accountSecretKey, TimeSpan timeTolerance)
{
var codes = new List<string>();
var iterationCounter = GetCurrentCounter();
var iterationOffset = 0;

if (timeTolerance.TotalSeconds > 30)
{
iterationOffset = Convert.ToInt32(timeTolerance.TotalSeconds / 30.00);
}

return GetCurrentPINs(accountSecretKey, iterationOffset);
}

/// <summary>
/// Get all the PINs that would be valid within the time window allowed for by the specified clock drift.
/// </summary>
/// <param name="accountSecretKey">Account Secret Key</param>
/// <param name="iterationOffset">The counter drift size you want to generate PINs for</param>
/// <returns></returns>
public string[] GetCurrentPINs(byte[] accountSecretKey, int iterationOffset)
{
var codes = new List<string>();
var iterationCounter = GetCurrentCounter();

var iterationStart = iterationCounter - iterationOffset;
var iterationEnd = iterationCounter + iterationOffset;

Expand Down

0 comments on commit 117ec8f

Please sign in to comment.