Skip to content

Commit

Permalink
GoogleAuth: fix 94 issue
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelbannov committed Feb 24, 2022
1 parent 6fb3547 commit d40d73a
Show file tree
Hide file tree
Showing 38 changed files with 2,266 additions and 1 deletion.
Binary file added .nuget/packages/GoogleAuthenticator.2.4.1.nupkg
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Text;
using Shouldly;
using Xunit;

namespace Google.Authenticator.Tests
{
public class AuthCodeTest
{
[Fact]
public void BasicAuthCodeTest()
{
var secretKey = "PJWUMZKAUUFQKJBAMD6VGJ6RULFVW4ZH";
var expected = "551508";

var tfa = new TwoFactorAuthenticator();

var currentTime = 1416643820;

// I actually think you are supposed to divide the time by 30 seconds?
// Maybe need an overload that takes a DateTime?
var actual = tfa.GeneratePINAtInterval(secretKey, currentTime, 6);

actual.ShouldBe(expected);
}

[Fact]
public void Base32AuthCodeTest()
{
var secretKey = Base32Encoding.ToString(Encoding.UTF8.GetBytes("PJWUMZKAUUFQKJBAMD6VGJ6RULFVW4ZH"));
var expected = "551508";

var tfa = new TwoFactorAuthenticator();

var currentTime = 1416643820;

// I actually think you are supposed to divide the time by 30 seconds?
// Maybe need an overload that takes a DateTime?
var actual = tfa.GeneratePINAtInterval(secretKey, currentTime, 6, true);

actual.ShouldBe(expected);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Xunit;
using Shouldly;
using System.Text;
using System.Net.NetworkInformation;

namespace Google.Authenticator.Tests
{
public class GeneratePinTests
{
[Fact]
public void OverloadsReturnSamePIN()
{
var secret = "JBSWY3DPEHPK3PXP";
var secretAsBytes = Encoding.UTF8.GetBytes(secret);
var secretAsBase32 = Base32Encoding.ToString(secretAsBytes);
long counter = 54615912;
var expected = "508826";

var subject = new TwoFactorAuthenticator();

var pinFromString = subject.GeneratePINAtInterval(secret, counter);
var pinFromBytes = subject.GeneratePINAtInterval(secretAsBytes, counter);
var pinFromBase32 = subject.GeneratePINAtInterval(secretAsBase32, counter, secretIsBase32: true);

pinFromString.ShouldBe(expected);
pinFromBytes.ShouldBe(expected);
pinFromBase32.ShouldBe(expected);
}
}
}

// private long GetCurrentCounter(DateTime now, DateTime epoch, int timeStep) =>
//(long) (now - epoch).TotalSeconds / timeStep;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net452;net5.0</TargetFrameworks>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ZXing.Net" Version="0.16.7" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net452'">
<PackageReference Include="ZXing.Net.Bindings.Magick" Version="0.16.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Google.Authenticator\Google.Authenticator.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Xunit;
using Shouldly;
using System.Diagnostics;
using System;
using ZXing;
using System.Collections.Generic;
using System.IO;

namespace Google.Authenticator.Tests
{
public class QRCodeTest
{
[Theory]
[InlineData("issuer", "otpauth://totp/issuer:[email protected]?secret=ONSWG4TFOQ&issuer=issuer")]
[InlineData("Foo & Bar", "otpauth://totp/Foo%20%26%20Bar:[email protected]?secret=ONSWG4TFOQ&issuer=Foo%20%26%20Bar")]
[InlineData("个", "otpauth://totp/%E4%B8%AA:[email protected]?secret=ONSWG4TFOQ&issuer=%E4%B8%AA")]
public void CanGenerateQRCode(string issuer, string expectedUrl)
{
var subject = new TwoFactorAuthenticator();
var setupCodeInfo = subject.GenerateSetupCode(
issuer,
"[email protected]",
"secret",
false,
2);

var actualUrl = ExtractUrlFromQRImage(setupCodeInfo.QrCodeSetupImageUrl);

actualUrl.ShouldBe(expectedUrl);
}

private static string ExtractUrlFromQRImage(string qrCodeSetupImageUrl)
{
var headerLength = "data:image/png;base64,".Length;
var rawImageData = qrCodeSetupImageUrl.Substring(headerLength, qrCodeSetupImageUrl.Length - headerLength);
var imageData = Convert.FromBase64String(rawImageData);

//var reader = new BarcodeReaderGeneric();
//reader.Options.PossibleFormats = new List<BarcodeFormat> {
// BarcodeFormat.QR_CODE
//};

#if NETFRAMEWORK
var reader = new BarcodeReader();
reader.Options.PossibleFormats = new List<BarcodeFormat> {
BarcodeFormat.QR_CODE
};
using (var ms = new MemoryStream(imageData))
{
var image = new System.Drawing.Bitmap(ms);
return reader.Decode(image).Text;
}
#else
var reader = new BarcodeReaderGeneric();
reader.Options.PossibleFormats = new List<BarcodeFormat> {
BarcodeFormat.QR_CODE
};
var image = new ImageMagick.MagickImage(imageData);
var wrappedImage = new ZXing.Magick.MagickImageLuminanceSource(image);
return reader.Decode(wrappedImage).Text;
#endif
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Xunit;
using Shouldly;
using System.Text;

namespace Google.Authenticator.Tests
{
public class SetupCodeTests
{
[Fact]
public void ByteAndStringGeneratesSameSetupCode()
{
var secret = "12345678901234567890123456789012";
var secretAsByteArray = Encoding.UTF8.GetBytes(secret);
var secretAsBase32 = Base32Encoding.ToString(secretAsByteArray);
var issuer = "Test";
var accountName = "TestAccount";
var expected = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA";

var subject = new TwoFactorAuthenticator();

var setupCodeFromString = subject.GenerateSetupCode(issuer, accountName, secret, false);
var setupCodeFromByteArray = subject.GenerateSetupCode(issuer, accountName, secretAsByteArray, 3, false);
var setupCodeFromBase32 = subject.GenerateSetupCode(issuer, accountName, secretAsBase32, true);

setupCodeFromString.ManualEntryKey.ShouldBe(expected);
setupCodeFromByteArray.ManualEntryKey.ShouldBe(expected);
setupCodeFromBase32.ManualEntryKey.ShouldBe(expected);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Xunit;
using Shouldly;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;

namespace Google.Authenticator.Tests
{
public class ValidationTests
{
const string secret = "ggggjhG&^*&^jfSSSddd";
private readonly static byte[] secretAsBytes = Encoding.UTF8.GetBytes(secret);
private readonly static string secretAsBase32 = Base32Encoding.ToString(secretAsBytes);

[Theory]
[MemberData(nameof(GetPins))]
public void ValidateWorksWithDifferentSecretTypes(string pin, int irrelevantNumberToAvoidDuplicatePinsBeingRemoved)
{
// We can't directly test that the different overloads for GetCurrentPIN creates the same result,
// as the time difference may may cause different PINS to be created.
// So instead we generate the PINs by each method and validate each one by each method.
var subject = new TwoFactorAuthenticator();

subject.ValidateTwoFactorPIN(secret, pin, false);
subject.ValidateTwoFactorPIN(secret, pin, TimeSpan.FromMinutes(irrelevantNumberToAvoidDuplicatePinsBeingRemoved), false);
subject.ValidateTwoFactorPIN(secretAsBytes, pin);
subject.ValidateTwoFactorPIN(secretAsBytes, pin, TimeSpan.FromMinutes(irrelevantNumberToAvoidDuplicatePinsBeingRemoved));
subject.ValidateTwoFactorPIN(secretAsBase32, pin, true);
subject.ValidateTwoFactorPIN(secretAsBase32, pin, TimeSpan.FromMinutes(irrelevantNumberToAvoidDuplicatePinsBeingRemoved), true);
}

public static IEnumerable<object[]> GetPins()
{
var subject = new TwoFactorAuthenticator();

yield return new object[] { subject.GetCurrentPIN(secret), 2 };
yield return new object[] { subject.GetCurrentPIN(secret, DateTime.UtcNow), 3 };
yield return new object[] { subject.GetCurrentPIN(secretAsBytes), 4 };
yield return new object[] { subject.GetCurrentPIN(secretAsBytes, DateTime.UtcNow), 5 };
yield return new object[] { subject.GetCurrentPIN(secretAsBase32, true), 6 };
yield return new object[] { subject.GetCurrentPIN(secretAsBase32, DateTime.UtcNow, true), 7 };
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Google.Authenticator.WebSample.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Google Authenticator Sample</title>
<style type="text/css">
body
{
font-family: Arial;
font-size: 14px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<strong>Account Secret Key (randomly generated):</strong> <asp:Label runat="server" ID="lblSecretKey"></asp:Label>
<hr />
<strong>Setup QR Code:</strong><br />
<asp:Image ID="imgQrCode" runat="server" /><br />
<br />
<strong>Manual Setup Code: </strong> <asp:Label runat="server" ID="lblManualSetupCode"></asp:Label>
<hr />
Validate Code: <asp:TextBox runat="server" ID="txtCode"></asp:TextBox> <asp:Button runat="server" ID="btnValidate" Text="Validate My Code!" OnClick="btnValidate_Click" /><br /><asp:Label runat="server" Font-Bold="true" ID="lblValidationResult"></asp:Label>
</div>
</form>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Google.Authenticator.WebSample
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Request.QueryString["key"]))
{
Response.Redirect("~/default.aspx?key=" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 10));
}

this.lblSecretKey.Text = Request.QueryString["key"];

TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
var setupInfo = tfa.GenerateSetupCode("我 & You", "[email protected]", Request.QueryString["key"], false, 10);

string qrCodeImageUrl = setupInfo.QrCodeSetupImageUrl;
string manualEntrySetupCode = setupInfo.ManualEntryKey;

this.imgQrCode.ImageUrl = qrCodeImageUrl;
this.lblManualSetupCode.Text = manualEntrySetupCode;
}

protected void btnValidate_Click(object sender, EventArgs e)
{
TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
var result = tfa.ValidateTwoFactorPIN(Request.QueryString["key"], this.txtCode.Text);

if (result)
{
this.lblValidationResult.Text = this.txtCode.Text + " is a valid PIN at UTC time " + DateTime.UtcNow.ToString();
this.lblValidationResult.ForeColor = System.Drawing.Color.Green;
}
else
{
this.lblValidationResult.Text = this.txtCode.Text + " is not a valid PIN at UTC time " + DateTime.UtcNow.ToString();
this.lblValidationResult.ForeColor = System.Drawing.Color.Red;
}
}
}
}
Loading

0 comments on commit d40d73a

Please sign in to comment.