mirror of
https://github.com/XFox111/SimpleOTP.git
synced 2026-04-22 08:00:45 +03:00
Major 2.0 (#20)
* New 2.0 version + DependencyInjection library * Updated docs and repo settings (devcontainers, vscode, github, etc.) * Added tests * Fixed bugs * Minor test project refactoring * Updated projects - Added symbol packages - Updated package versions to pre-release * Updated SECURITY.md * Added GitHub Actions workflows
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace SimpleOTP.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class ConfigTests
|
||||
{
|
||||
private static readonly byte[] secretBytes = [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0xde, 0xad, 0xbe, 0xef];
|
||||
private const string username = "eugene@xfox111.net";
|
||||
private const string appName = "Example App";
|
||||
private const string issuer = "example.com";
|
||||
|
||||
|
||||
[TestCase("otpauth://totp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA1&digits=6&period=30",
|
||||
"SHA1", OtpType.Totp, 6, 30, null)]
|
||||
[TestCase("otpauth://totp/Example%20App:eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA1&digits=6&period=30",
|
||||
"SHA1", OtpType.Totp, 6, 30, appName)]
|
||||
[TestCase("apple-otpauth://totp/Example%20App:eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA1&digits=6&period=30",
|
||||
"SHA1", OtpType.Totp, 6, 30, appName)]
|
||||
[TestCase("otpauth://totp/Example%20App:eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA256&digits=8&period=60",
|
||||
"SHA256", OtpType.Totp, 8, 60, appName)]
|
||||
[TestCase("otpauth://totp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA512&digits=6&period=30",
|
||||
"SHA512", OtpType.Totp, 6, 30, null)]
|
||||
[TestCase("otpauth://hotp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA512&digits=6&counter=0",
|
||||
"SHA512", OtpType.Hotp, 6, 30, null)]
|
||||
[TestCase("otpauth://hotp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=HmacSHA512&digits=6&counter=0",
|
||||
"SHA512", OtpType.Hotp, 6, 30, null)]
|
||||
public void ParseTest(string uri, string algorithm, OtpType type, int digits, int period, string? appName)
|
||||
{
|
||||
OtpConfig config = OtpConfig.ParseUri(uri);
|
||||
|
||||
Assert.That(config.Label, Is.EqualTo(username));
|
||||
Assert.That(config.Secret, Is.EqualTo(secretBytes));
|
||||
Assert.That(config.Issuer, Is.EqualTo(issuer));
|
||||
Assert.That(config.IssuerLabel, Is.EqualTo(appName));
|
||||
Assert.That(config.Algorithm, Is.EqualTo(algorithm));
|
||||
Assert.That(config.Digits, Is.EqualTo(digits));
|
||||
Assert.That(config.Period, Is.EqualTo(period));
|
||||
Assert.That(config.Type, Is.EqualTo(type));
|
||||
}
|
||||
|
||||
[TestCase(OtpUriFormat.Google | OtpUriFormat.Minimal, false,
|
||||
"otpauth://totp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com")]
|
||||
[TestCase(OtpUriFormat.Google | OtpUriFormat.Full, false,
|
||||
"otpauth://totp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA1&digits=6&period=30")]
|
||||
[TestCase(OtpUriFormat.Apple | OtpUriFormat.Minimal, true,
|
||||
"apple-otpauth://totp/Example%20App:eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com")]
|
||||
[TestCase(OtpUriFormat.Apple | OtpUriFormat.Full, true,
|
||||
"apple-otpauth://totp/Example%20App:eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=SHA1&digits=6&period=30")]
|
||||
[TestCase(OtpUriFormat.IBM | OtpUriFormat.Full, false,
|
||||
"otpauth://totp/eugene%40xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=example.com&algorithm=HmacSHA1&digits=6&period=30")]
|
||||
public void ToUriTest(OtpUriFormat format, bool appendIssuerLabel, string expectedUri)
|
||||
{
|
||||
OtpConfig config = new(username)
|
||||
{
|
||||
Issuer = issuer,
|
||||
Secret = OtpSecret.FromBytes(secretBytes)
|
||||
};
|
||||
|
||||
if (appendIssuerLabel)
|
||||
config.IssuerLabel = appName;
|
||||
|
||||
Uri uri = config.ToUri(format);
|
||||
Assert.That(uri.AbsoluteUri, Is.EqualTo(expectedUri));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="NUnit" Version="4.2.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libraries\SimpleOTP\SimpleOTP.csproj" />
|
||||
<ProjectReference Include="..\libraries\SimpleOTP.DependencyInjection\SimpleOTP.DependencyInjection.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,63 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace SimpleOTP.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TotpTests
|
||||
{
|
||||
private const string secret1 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ";
|
||||
private const string secret2 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA";
|
||||
private const string secret3 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA";
|
||||
|
||||
[TestCase(secret1, "SHA1", 59, "94287082", 60)]
|
||||
[TestCase(secret2, "SHA256", 59, "46119246", 60)]
|
||||
[TestCase(secret3, "SHA512", 59, "90693936", 60)]
|
||||
[TestCase(secret1, "SHA1", 1111111109, "07081804", 1111111110)]
|
||||
[TestCase(secret2, "SHA256", 1111111109, "68084774", 1111111110)]
|
||||
[TestCase(secret3, "SHA512", 1111111109, "25091201", 1111111110)]
|
||||
[TestCase(secret1, "SHA1", 1111111111, "14050471", 1111111140)]
|
||||
[TestCase(secret2, "SHA256", 1111111111, "67062674", 1111111140)]
|
||||
[TestCase(secret3, "SHA512", 1111111111, "99943326", 1111111140)]
|
||||
[TestCase(secret1, "SHA1", 1234567890, "89005924", 1234567920)]
|
||||
[TestCase(secret2, "SHA256", 1234567890, "91819424", 1234567920)]
|
||||
[TestCase(secret3, "SHA512", 1234567890, "93441116", 1234567920)]
|
||||
[TestCase(secret1, "SHA1", 2000000000, "69279037", 2000000010)]
|
||||
[TestCase(secret2, "SHA256", 2000000000, "90698825", 2000000010)]
|
||||
[TestCase(secret3, "SHA512", 2000000000, "38618901", 2000000010)]
|
||||
[TestCase(secret1, "SHA1", 20000000000, "65353130", 20000000010)]
|
||||
[TestCase(secret2, "SHA256", 20000000000, "77737706", 20000000010)]
|
||||
[TestCase(secret3, "SHA512", 20000000000, "47863826", 20000000010)]
|
||||
[TestCase(secret1, "SHA1", 20000000000, "353130", 20000000010)]
|
||||
[TestCase(secret2, "SHA256", 20000000000, "737706", 20000000010)]
|
||||
[TestCase(secret3, "SHA512", 20000000000, "863826", 20000000010)]
|
||||
public void ComputeTest(string secret, string algorithm, long timestamp, string expectedOtp, long expectedExpiration)
|
||||
{
|
||||
using OtpSecret otpSecret = OtpSecret.Parse(secret);
|
||||
Totp totp = new(otpSecret, 30, (OtpAlgorithm)algorithm, expectedOtp.Length);
|
||||
DateTimeOffset time = DateTimeOffset.FromUnixTimeSeconds(timestamp);
|
||||
OtpCode code = totp.Generate(time);
|
||||
|
||||
Assert.That(code.ToString(), Is.EqualTo(expectedOtp));
|
||||
Assert.That(code.ExpirationTime, Is.EqualTo(DateTimeOffset.FromUnixTimeSeconds(expectedExpiration)));
|
||||
}
|
||||
|
||||
[TestCase(59, "287082", 1, true, 0)]
|
||||
[TestCase(0, "287082", 1, true, 1)]
|
||||
[TestCase(10, "287082", 0, false, 0)]
|
||||
[TestCase(20000000000, "353130", 1, true, 0)]
|
||||
[TestCase(20000000030, "353130", 0, false, 0)]
|
||||
[TestCase(20000000030, "353130", 1, true, -1)]
|
||||
[TestCase(20000000060, "353130", 1, false, 0)]
|
||||
[TestCase(20000000060, "353130", 2, true, -2)]
|
||||
public void ValidateOffsetTest(long timestamp, string code, int toleranceWindow, bool expectedResult, int expectedResync)
|
||||
{
|
||||
using OtpSecret secret = OtpSecret.Parse(secret1);
|
||||
Totp totp = new(secret, 30, code.Length);
|
||||
DateTimeOffset time = DateTimeOffset.FromUnixTimeSeconds(timestamp);
|
||||
|
||||
bool result = totp.Validate((OtpCode)code, toleranceWindow, time, out int resyncValue);
|
||||
|
||||
Assert.That(result, Is.EqualTo(expectedResult));
|
||||
Assert.That(resyncValue, Is.EqualTo(expectedResync));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user