Skip to content

Commit

Permalink
Add constructors to use the OTP classes with a generic IKeyProvider (#24
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mtausig authored and kspearrin committed Oct 29, 2019
1 parent bbb640a commit 8bebfc5
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 2 deletions.
14 changes: 14 additions & 0 deletions src/Otp.NET/Hotp.cs
Expand Up @@ -52,6 +52,20 @@ public Hotp(byte[] secretKey, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize
this.hotpSize = hotpSize;
}

/// <summary>
/// Create a HOTP instance
/// </summary>
/// <param name="key">The key to use in HOTP calculations</param>
/// <param name="mode">The hash mode to use</param>
/// <param name="hotpSize">The number of digits that the returning HOTP should have. The default is 6.</param>
public Hotp(IKeyProvider key, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize = 6)
: base(key, mode)
{
VerifyParameters(hotpSize);

this.hotpSize = hotpSize;
}

private static void VerifyParameters(int hotpSize)
{
if(!(hotpSize >= 6))
Expand Down
17 changes: 16 additions & 1 deletion src/Otp.NET/Otp.cs
Expand Up @@ -47,7 +47,7 @@ public abstract class Otp
protected readonly OtpHashMode hashMode;

/// <summary>
/// Constructor for the abstract class. This is to guarantee that all implementations have a secret key
/// Constructor for the abstract class using an explicit secret key
/// </summary>
/// <param name="secretKey"></param>
/// <param name="mode">The hash mode to use</param>
Expand All @@ -64,6 +64,21 @@ public Otp(byte[] secretKey, OtpHashMode mode)
this.hashMode = mode;
}

/// <summary>
/// Constructor for the abstract class using a generic key provider
/// </summary>
/// <param name="key"></param>
/// <param name="mode">The hash mode to use</param>
public Otp(IKeyProvider key, OtpHashMode mode)
{
if (key == null)
throw new ArgumentNullException("key");

this.secretKey = key;

this.hashMode = mode;
}

/// <summary>
/// An abstract definition of a compute method. Takes a counter and runs it through the derived algorithm.
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions src/Otp.NET/Totp.cs
Expand Up @@ -70,6 +70,26 @@ public Totp(byte[] secretKey, int step = 30, OtpHashMode mode = OtpHashMode.Sha1
this.correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance;
}

/// <summary>
/// Create a TOTP instance
/// </summary>
/// <param name="key">The secret key to use in TOTP calculations</param>
/// <param name="step">The time window step amount to use in calculating time windows. The default is 30 as recommended in the RFC</param>
/// <param name="mode">The hash mode to use</param>
/// <param name="totpSize">The number of digits that the returning TOTP should have. The default is 6.</param>
/// <param name="timeCorrection">If required, a time correction can be specified to compensate of an out of sync local clock</param>
public Totp(IKeyProvider key, int step = 30, OtpHashMode mode = OtpHashMode.Sha1, int totpSize = 6, TimeCorrection timeCorrection = null)
: base(key, mode)
{
VerifyParameters(step, totpSize);

this.step = step;
this.totpSize = totpSize;

// we never null check the corrected time object. Since it's readonly, we'll ensure that it isn't null here and provide neatral functionality in this case.
this.correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance;
}

private static void VerifyParameters(int step, int totpSize)
{
if(!(step > 0))
Expand Down
15 changes: 14 additions & 1 deletion test/Otp.NET.Test/HotpTest.cs
@@ -1,4 +1,5 @@
using NUnit.Framework;
using Moq;
using NUnit.Framework;

namespace OtpNet.Test
{
Expand Down Expand Up @@ -27,5 +28,17 @@ public void ComputeHOTPRfc4226Test(OtpHashMode hash, long counter, string expect
string otp = otpCalc.ComputeHOTP(counter);
Assert.That(otp, Is.EqualTo(expectedOtp));
}

[Test()]
public void ContructorWithKeyProviderTest()
{
//Mock a key provider which always returns an all-zero HMAC (causing an all-zero OTP)
Mock<IKeyProvider> keyMock = new Mock<IKeyProvider>();
keyMock.Setup(key => key.ComputeHmac(It.Is<OtpHashMode>(m => m == OtpHashMode.Sha1), It.IsAny<byte[]>())).Returns(new byte[20]);

var otp = new Hotp(keyMock.Object, OtpHashMode.Sha1, 6);
Assert.That(otp.ComputeHOTP(0), Is.EqualTo("000000"));
Assert.That(otp.ComputeHOTP(1), Is.EqualTo("000000"));
}
}
}
1 change: 1 addition & 0 deletions test/Otp.NET.Test/Otp.NET.Test.csproj
Expand Up @@ -10,6 +10,7 @@
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Moq" Version="4.13.1" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions test/Otp.NET.Test/TotpTest.cs
@@ -1,5 +1,6 @@
using System;
using System.Text;
using Moq;
using NUnit.Framework;

namespace OtpNet.Test
Expand Down Expand Up @@ -36,5 +37,16 @@ public void ComputeTOTPTest(string secret, OtpHashMode hash, long timestamp, str
string otp = otpCalc.ComputeTotp(time);
Assert.That(otp, Is.EqualTo(expectedOtp));
}

[Test()]
public void ContructorWithKeyProviderTest()
{
//Mock a key provider which always returns an all-zero HMAC (causing an all-zero OTP)
Mock<IKeyProvider> keyMock = new Mock<IKeyProvider>();
keyMock.Setup(key => key.ComputeHmac(It.Is<OtpHashMode>(m => m == OtpHashMode.Sha1), It.IsAny<byte[]>())).Returns(new byte[20]);

var otp = new Totp(keyMock.Object, 30, OtpHashMode.Sha1, 6);
Assert.That(otp.ComputeTotp(), Is.EqualTo("000000"));
}
}
}

0 comments on commit 8bebfc5

Please sign in to comment.