// ------------------------------------------------------------ // Copyright ©2021 Eugene Fox. All rights reserved. // Code by Eugene Fox (aka XFox) // // Licensed under MIT license (https://opensource.org/licenses/MIT) // ------------------------------------------------------------ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using SimpleOTP.Models; namespace SimpleOTP.Test { /// /// Unit-tests for OTP generator. /// [TestClass] public class OTPServiceUnitTest { private readonly DateTime time = new (2021, 5, 28, 10, 47, 50, DateTimeKind.Utc); private OTPConfiguration totpConfig = OTPConfiguration.GetConfiguration(new Uri("otpauth://totp/FoxDev%20Studio:eugene@xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=FoxDev%20Studio")); private OTPConfiguration hotpConfig = OTPConfiguration.GetConfiguration(new Uri("otpauth://hotp/FoxDev%20Studio:eugene@xfox111.net?secret=JBSWY3DPEHPK3PXP&issuer=FoxDev%20Studio&counter=10000")); /// /// Test time-based OTP generator with pre-calculated code. /// [TestMethod("TOTP code generation")] public void GenerateCode_Totp() { var config = totpConfig with { }; var code = OTPService.GenerateCode(ref config, time); Assert.AreEqual(160102, code.Code); config.Algorithm = Enums.Algorithm.SHA256; code = OTPService.GenerateCode(ref config, time); Assert.AreEqual(195671, code.Code); config.Algorithm = Enums.Algorithm.SHA512; code = OTPService.GenerateCode(ref config, time); Assert.AreEqual(293657, code.Code); } /// /// Test HOTP generator with pre-calculated code. /// [TestMethod("HOTP code generation")] public void GenerateCode_Hotp() { var code = OTPService.GenerateCode(ref hotpConfig); Assert.AreEqual(457608, code.Code); Assert.AreEqual(10001, hotpConfig.Counter); } /// /// Test time-based OTP validator with series of codes. /// [TestMethod("TOTP code validation")] public void ValidateCode_Totp() { int[] codes = { OTPService.GenerateCode(ref totpConfig, DateTime.UtcNow.AddSeconds(-45)).Code, OTPService.GenerateCode(ref totpConfig, DateTime.UtcNow.AddSeconds(-15)).Code, OTPService.GenerateCode(ref totpConfig, DateTime.UtcNow.AddSeconds(0)).Code, OTPService.GenerateCode(ref totpConfig, DateTime.UtcNow.AddSeconds(15)).Code, OTPService.GenerateCode(ref totpConfig, DateTime.UtcNow.AddSeconds(45)).Code, }; Assert.IsFalse(OTPService.ValidateCode(codes[0], totpConfig)); Assert.IsTrue(OTPService.ValidateCode(codes[1], totpConfig)); Assert.IsTrue(OTPService.ValidateCode(codes[2], totpConfig)); Assert.IsTrue(OTPService.ValidateCode(codes[3], totpConfig)); Assert.IsFalse(OTPService.ValidateCode(codes[4], totpConfig)); Assert.IsTrue(OTPService.ValidateCode(codes[0], totpConfig, TimeSpan.FromSeconds(60))); } /// /// Test HOTP validator with series of codes. /// [TestMethod("HOTP code validation")] public void ValidateCode_Hotp() { hotpConfig.Counter = 10000; int[] codes = { OTPService.GenerateCode(ref hotpConfig).Code, OTPService.GenerateCode(ref hotpConfig).Code, OTPService.GenerateCode(ref hotpConfig).Code, OTPService.GenerateCode(ref hotpConfig).Code, OTPService.GenerateCode(ref hotpConfig).Code, }; hotpConfig.Counter = 10002; Assert.IsFalse(OTPService.ValidateCode(codes[0], ref hotpConfig, 1, true)); Assert.AreEqual(10002, hotpConfig.Counter); Assert.IsTrue(OTPService.ValidateCode(codes[1], ref hotpConfig, 1, true)); Assert.AreEqual(10001, hotpConfig.Counter); hotpConfig.Counter = 10002; Assert.IsTrue(OTPService.ValidateCode(codes[2], ref hotpConfig, 1, true)); Assert.AreEqual(10002, hotpConfig.Counter); hotpConfig.Counter = 10002; Assert.IsTrue(OTPService.ValidateCode(codes[3], ref hotpConfig, 1, true)); Assert.AreEqual(10003, hotpConfig.Counter); hotpConfig.Counter = 10002; Assert.IsFalse(OTPService.ValidateCode(codes[4], ref hotpConfig, 1, true)); Assert.AreEqual(10002, hotpConfig.Counter); } } }