diff options
author | Guillaume Lacasa <guillaume.lacasa@ucaya.com> | 2013-12-06 17:33:34 +0100 |
---|---|---|
committer | Guillaume Lacasa <guillaume.lacasa@ucaya.com> | 2013-12-06 17:33:34 +0100 |
commit | cb801c254bfbafe80a1f3cd093a3dfe1d01538cb (patch) | |
tree | 4966d374878163c4a5c894c25f9cb8c0e61f11f1 | |
parent | 93adb5b673c1cf6d3abdb287d349ffad11ed7665 (diff) | |
parent | 57f740845da867fc270d9b185508bc5761aa7d45 (diff) | |
download | TwoStepsAuthenticator-cb801c254bfbafe80a1f3cd093a3dfe1d01538cb.zip TwoStepsAuthenticator-cb801c254bfbafe80a1f3cd093a3dfe1d01538cb.tar.gz TwoStepsAuthenticator-cb801c254bfbafe80a1f3cd093a3dfe1d01538cb.tar.bz2 |
Merge branch 'master' of https://github.com/dusk0r/TwoStepsAuthenticator
Conflicts:
README.md
TwoStepsAuthenticator.UnitTests/UsedCodesManagerTests.cs
TwoStepsAuthenticator/CounterAuthenticator.cs
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | TwoStepsAuthenticator.UnitTests/CounterAuthenticatorTests.cs | 23 | ||||
-rw-r--r-- | TwoStepsAuthenticator.UnitTests/TimeAuthenticatorTests.cs | 13 | ||||
-rw-r--r-- | TwoStepsAuthenticator.UnitTests/UsedCodesManagerTests.cs | 6 | ||||
-rw-r--r-- | TwoStepsAuthenticator/Authenticator.cs | 1 | ||||
-rw-r--r-- | TwoStepsAuthenticator/CounterAuthenticator.cs | 13 | ||||
-rw-r--r-- | TwoStepsAuthenticator/IUsedCodesManager.cs | 4 | ||||
-rw-r--r-- | TwoStepsAuthenticator/TimeAuthenticator.cs | 6 | ||||
-rw-r--r-- | TwoStepsAuthenticator/UsedCodesManager.cs | 20 |
9 files changed, 42 insertions, 48 deletions
@@ -41,6 +41,7 @@ var authenticator = new TwoStepsAuthenticator.TimeAuthenticator(); bool isok = authenticator.CheckCode(secret, code); </code></pre> +<<<<<<< HEAD ### Used codes manager Every code should only be used once. To prevent repeated use of a code a IUsedCodesManager interface is provided.<br> @@ -59,3 +60,6 @@ When you create a new Authenticator, add the instance of your IUsedCodesManager var usedCodeManager = new CustomUsedCodeManager(); var authenticator = new TwoStepsAuthenticator.TimeAuthenticator(usedCodeManager); </code></pre> +======= +Every time-based code should only be used once. A built-in mechanism ensures that. If you want to control this check by your self you can pass in an instance of IUsedCodesManager. +>>>>>>> 57f740845da867fc270d9b185508bc5761aa7d45 diff --git a/TwoStepsAuthenticator.UnitTests/CounterAuthenticatorTests.cs b/TwoStepsAuthenticator.UnitTests/CounterAuthenticatorTests.cs index 3a81a90..f834178 100644 --- a/TwoStepsAuthenticator.UnitTests/CounterAuthenticatorTests.cs +++ b/TwoStepsAuthenticator.UnitTests/CounterAuthenticatorTests.cs @@ -9,27 +9,10 @@ namespace TwoStepsAuthenticator.UnitTests { [TestFixture] public class CounterAuthenticatorTests { - private MockUsedCodesManager mockUsedCodesManager { get; set; } - - [SetUp] - public void SetUp() { - this.mockUsedCodesManager = new MockUsedCodesManager(); - } - - [Test] - public void Uses_usedCodesManager() { - var authenticator = new CounterAuthenticator(usedCodeManager: mockUsedCodesManager); - var secret = Authenticator.GenerateKey(); - var code = authenticator.GetCode(secret, 42uL); - - authenticator.CheckCode(secret, code, 42uL); - Assert.AreEqual(mockUsedCodesManager.LastChallenge, 42uL); - Assert.AreEqual(mockUsedCodesManager.LastCode, code); - } [Test] public void CreateKey() { - var authenticator = new CounterAuthenticator(usedCodeManager: mockUsedCodesManager); + var authenticator = new CounterAuthenticator(); var secret = Authenticator.GenerateKey(); var code = authenticator.GetCode(secret, 0uL); @@ -48,7 +31,7 @@ namespace TwoStepsAuthenticator.UnitTests { [TestCase("12345678901234567890", 8uL, "399871")] [TestCase("12345678901234567890", 9uL, "520489")] public void VerifyKeys(string secret, ulong counter, string code) { - var authenticator = new CounterAuthenticator(usedCodeManager: mockUsedCodesManager); + var authenticator = new CounterAuthenticator(); var base32Secret = Base32Encoding.ToString(Encoding.ASCII.GetBytes(secret)); Assert.IsTrue(authenticator.CheckCode(base32Secret, code, counter)); @@ -57,7 +40,7 @@ namespace TwoStepsAuthenticator.UnitTests { [Test] public void VerifyUsedCounter() { - var authenticator = new CounterAuthenticator(usedCodeManager: mockUsedCodesManager); + var authenticator = new CounterAuthenticator(); // Test Values from http://www.ietf.org/rfc/rfc4226.txt - Appendix D var base32Secret = Base32Encoding.ToString(Encoding.ASCII.GetBytes("12345678901234567890")); diff --git a/TwoStepsAuthenticator.UnitTests/TimeAuthenticatorTests.cs b/TwoStepsAuthenticator.UnitTests/TimeAuthenticatorTests.cs index 55be883..99d1957 100644 --- a/TwoStepsAuthenticator.UnitTests/TimeAuthenticatorTests.cs +++ b/TwoStepsAuthenticator.UnitTests/TimeAuthenticatorTests.cs @@ -37,10 +37,23 @@ namespace TwoStepsAuthenticator.UnitTests var code = authenticator.GetCode(secret); authenticator.CheckCode(secret, code); + Assert.AreEqual(mockUsedCodesManager.LastChallenge, 0uL); Assert.AreEqual(mockUsedCodesManager.LastCode, code); } + [Test] + public void Prevent_code_reuse() { + var date = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + var usedCodesManager = new UsedCodesManager(); + var authenticator = new TimeAuthenticator(() => date, usedCodeManager: usedCodesManager); + var secret = Authenticator.GenerateKey(); + var code = authenticator.GetCode(secret); + + Assert.IsTrue(authenticator.CheckCode(secret, code)); + Assert.IsFalse(authenticator.CheckCode(secret, code)); + } + // Test Vectors from http://tools.ietf.org/html/rfc6238#appendix-B have all length 8. We want a length of 6. // This Test Vectors are from a Ruby implementation. They work with the Google Authentificator app. [TestCase("DRMK64PPMMC7TDZF", "2013-12-04 18:33:01 +0100", "661188")] diff --git a/TwoStepsAuthenticator.UnitTests/UsedCodesManagerTests.cs b/TwoStepsAuthenticator.UnitTests/UsedCodesManagerTests.cs index 15ae91e..d72474c 100644 --- a/TwoStepsAuthenticator.UnitTests/UsedCodesManagerTests.cs +++ b/TwoStepsAuthenticator.UnitTests/UsedCodesManagerTests.cs @@ -14,9 +14,9 @@ namespace TwoStepsAuthenticator.UnitTests { public void Can_add_codes() { var manager = new UsedCodesManager(); - Assert.IsFalse(manager.IsCodeUsed(42uL, "def")); - manager.AddCode(42uL, "def"); - Assert.IsTrue(manager.IsCodeUsed(42uL, "def")); + Assert.IsFalse(manager.IsCodeUsed(42L, "def")); + manager.AddCode(42L, "def"); + Assert.IsTrue(manager.IsCodeUsed(42L, "def")); } } diff --git a/TwoStepsAuthenticator/Authenticator.cs b/TwoStepsAuthenticator/Authenticator.cs index 4415e77..eea3612 100644 --- a/TwoStepsAuthenticator/Authenticator.cs +++ b/TwoStepsAuthenticator/Authenticator.cs @@ -12,7 +12,6 @@ namespace TwoStepsAuthenticator private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider(); // Is Thread-Safe private static readonly int KeyLength = 16; private static readonly string AvailableKeyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - internal static readonly Lazy<IUsedCodesManager> DefaultUsedCodeManager = new Lazy<IUsedCodesManager>(() => new UsedCodesManager()); public static string GenerateKey() { var keyChars = new char[KeyLength]; diff --git a/TwoStepsAuthenticator/CounterAuthenticator.cs b/TwoStepsAuthenticator/CounterAuthenticator.cs index 7fad752..56f1319 100644 --- a/TwoStepsAuthenticator/CounterAuthenticator.cs +++ b/TwoStepsAuthenticator/CounterAuthenticator.cs @@ -13,16 +13,12 @@ namespace TwoStepsAuthenticator public class CounterAuthenticator : Authenticator { private readonly int WindowSize; - private readonly IUsedCodesManager UsedCodeManager; - public CounterAuthenticator(IUsedCodesManager usedCodeManager = null, int windowSize = 10) - { - if (windowSize <= 0) - { + public CounterAuthenticator(int windowSize = 10) { + if (windowSize <= 0) { throw new ArgumentException("look-ahead window size must be positive"); } - this.UsedCodeManager = (usedCodeManager == null) ? Authenticator.DefaultUsedCodeManager.Value : usedCodeManager; this.WindowSize = windowSize; } @@ -67,12 +63,9 @@ namespace TwoStepsAuthenticator for (uint i = 0; i <= WindowSize; i++) { ulong checkCounter = counter + i; - if (ConstantTimeEquals(GetCode(secret, checkCounter), code) && !UsedCodeManager.IsCodeUsed(checkCounter, code)) - { + if (ConstantTimeEquals(GetCode(secret, checkCounter), code)) { codeMatch = true; successfulSequenceNumber = checkCounter; - - UsedCodeManager.AddCode(successfulSequenceNumber, code); } } diff --git a/TwoStepsAuthenticator/IUsedCodesManager.cs b/TwoStepsAuthenticator/IUsedCodesManager.cs index 461975c..8ff1fe3 100644 --- a/TwoStepsAuthenticator/IUsedCodesManager.cs +++ b/TwoStepsAuthenticator/IUsedCodesManager.cs @@ -12,7 +12,7 @@ namespace TwoStepsAuthenticator { /// </summary> /// <param name="challenge">Used Challenge</param> /// <param name="code">Used Code</param> - void AddCode(ulong challenge, string code); + void AddCode(long timestamp, string code); /// <summary> /// Checks if code was previously used. @@ -20,6 +20,6 @@ namespace TwoStepsAuthenticator { /// <param name="challenge">Used Challenge</param> /// <param name="code">Used Code</param> /// <returns></returns> - bool IsCodeUsed(ulong challenge, string code); + bool IsCodeUsed(long timestamp, string code); } } diff --git a/TwoStepsAuthenticator/TimeAuthenticator.cs b/TwoStepsAuthenticator/TimeAuthenticator.cs index 4966926..8c42e41 100644 --- a/TwoStepsAuthenticator/TimeAuthenticator.cs +++ b/TwoStepsAuthenticator/TimeAuthenticator.cs @@ -10,13 +10,15 @@ namespace TwoStepsAuthenticator { /// Implementation of rfc6238 Time-Based One-Time Password Algorithm /// </summary> public class TimeAuthenticator : Authenticator { + private static readonly Lazy<IUsedCodesManager> DefaultUsedCodeManager = new Lazy<IUsedCodesManager>(() => new UsedCodesManager()); + private readonly Func<DateTime> NowFunc; private readonly IUsedCodesManager UsedCodeManager; private readonly int IntervalSeconds; public TimeAuthenticator(IUsedCodesManager usedCodeManager = null, Func<DateTime> nowFunc = null, int intervalSeconds = 30) { this.NowFunc = (nowFunc == null) ? () => DateTime.Now : nowFunc; - this.UsedCodeManager = (usedCodeManager == null) ? Authenticator.DefaultUsedCodeManager.Value : usedCodeManager; + this.UsedCodeManager = (usedCodeManager == null) ? DefaultUsedCodeManager.Value : usedCodeManager; this.IntervalSeconds = intervalSeconds; } @@ -66,7 +68,7 @@ namespace TwoStepsAuthenticator { var codeMatch = false; for (int i = -2; i <= 1; i++) { var checkTime = baseTime.AddSeconds(IntervalSeconds * i); - ulong checkInterval = (ulong)GetInterval(checkTime); + var checkInterval = GetInterval(checkTime); if (ConstantTimeEquals(GetCode(secret, checkTime), code) && !UsedCodeManager.IsCodeUsed(checkInterval, code)) { codeMatch = true; diff --git a/TwoStepsAuthenticator/UsedCodesManager.cs b/TwoStepsAuthenticator/UsedCodesManager.cs index 6ffd6cf..13b3d61 100644 --- a/TwoStepsAuthenticator/UsedCodesManager.cs +++ b/TwoStepsAuthenticator/UsedCodesManager.cs @@ -14,15 +14,15 @@ namespace TwoStepsAuthenticator { internal sealed class UsedCode { - public UsedCode(ulong challenge, String code) + public UsedCode(long timestamp, String code) { this.UseDate = DateTime.Now; this.Code = code; - this.ChallengeValue = challenge; + this.Timestamp = timestamp; } internal DateTime UseDate { get; private set; } - internal ulong ChallengeValue { get; private set; } + internal long Timestamp { get; private set; } internal String Code { get; private set; } public override bool Equals(object obj) @@ -32,15 +32,15 @@ namespace TwoStepsAuthenticator } var other = obj as UsedCode; - return (other != null) ? this.Code.Equals(other.Code) && this.ChallengeValue.Equals(other.ChallengeValue) : false; + return (other != null) ? this.Code.Equals(other.Code) && this.Timestamp.Equals(other.Timestamp) : false; } public override string ToString() { - return String.Format("{0}: {1}", ChallengeValue, Code); + return String.Format("{0}: {1}", Timestamp, Code); } public override int GetHashCode() { - return Code.GetHashCode() + ChallengeValue.GetHashCode() * 17; + return Code.GetHashCode() + Timestamp.GetHashCode() * 17; } } @@ -75,12 +75,12 @@ namespace TwoStepsAuthenticator } } - public void AddCode(ulong challenge, String code) + public void AddCode(long timestamp, String code) { try { rwlock.AcquireWriterLock(lockingTimeout); - codes.Enqueue(new UsedCode(challenge, code)); + codes.Enqueue(new UsedCode(timestamp, code)); } finally { @@ -88,13 +88,13 @@ namespace TwoStepsAuthenticator } } - public bool IsCodeUsed(ulong challenge, String code) + public bool IsCodeUsed(long timestamp, String code) { try { rwlock.AcquireReaderLock(lockingTimeout); - return codes.Contains(new UsedCode(challenge, code)); + return codes.Contains(new UsedCode(timestamp, code)); } finally { |