diff --git a/SimpleOTP.Test/Helpers/Base32UnitTest.cs b/SimpleOTP.Test/Helpers/Base32UnitTest.cs
index 98f38c7..aac23d2 100644
--- a/SimpleOTP.Test/Helpers/Base32UnitTest.cs
+++ b/SimpleOTP.Test/Helpers/Base32UnitTest.cs
@@ -6,6 +6,7 @@
// ------------------------------------------------------------
using System;
+using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SimpleOTP.Helpers;
@@ -19,19 +20,33 @@ namespace SimpleOTP.Test.Helpers
public class Base32UnitTest
{
///
- /// Test overall work of the encoder.
+ /// Test encoder with byte array.
///
- [TestMethod("Overall Base32 encoder test")]
+ [TestMethod("Byte array Base32 encoder test")]
public void EncoderTest()
{
- // byte[] bytes = new byte[new Random().Next(128, 161)]; // FIXME: See SimpleOTP.Helpers.Base32Encoder.Encode()
- byte[] bytes = new byte[160];
+ byte[] bytes = new byte[new Random().Next(16, 20)];
new Random().NextBytes(bytes);
string str = Base32Encoder.Encode(bytes);
- bytes = Base32Encoder.Decode(str);
- string result = Base32Encoder.Encode(bytes);
- Assert.AreEqual(str, result);
+ byte[] result = Base32Encoder.Decode(str);
+ Assert.AreEqual(bytes.Length, result.Length);
+ for (int i = 0; i < bytes.Length; i++)
+ Assert.AreEqual(bytes[i], result[i]);
+ }
+
+ ///
+ /// Test encoder with string content.
+ ///
+ [TestMethod("String Base32 encoder test")]
+ public void EncoderStringTest()
+ {
+ string testStr = "Hello, World!";
+ string encodedStr = Base32Encoder.Encode(Encoding.UTF8.GetBytes(testStr));
+
+ byte[] resultBytes = Base32Encoder.Decode(encodedStr);
+ string result = Encoding.UTF8.GetString(resultBytes);
+ Assert.AreEqual(testStr, result);
}
}
}
\ No newline at end of file
diff --git a/SimpleOTP.Test/Models/OTPConfigurationUnitTest.cs b/SimpleOTP.Test/Models/OTPConfigurationUnitTest.cs
index 4a2ca9b..99ec584 100644
--- a/SimpleOTP.Test/Models/OTPConfigurationUnitTest.cs
+++ b/SimpleOTP.Test/Models/OTPConfigurationUnitTest.cs
@@ -87,4 +87,4 @@ namespace SimpleOTP.Test.Models
"yEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHE/gcIt5Gg5RNZHAAAAABJRU5ErkJggg==", imageStr);
}
}
-}
+}
\ No newline at end of file
diff --git a/SimpleOTP.Test/SimpleOTP.Test.csproj b/SimpleOTP.Test/SimpleOTP.Test.csproj
index 3928089..7b2780f 100644
--- a/SimpleOTP.Test/SimpleOTP.Test.csproj
+++ b/SimpleOTP.Test/SimpleOTP.Test.csproj
@@ -24,8 +24,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/SimpleOTP/Helpers/Base32Encoder.cs b/SimpleOTP/Helpers/Base32Encoder.cs
index f68f109..9aacc65 100644
--- a/SimpleOTP/Helpers/Base32Encoder.cs
+++ b/SimpleOTP/Helpers/Base32Encoder.cs
@@ -5,7 +5,8 @@
// Licensed under MIT license (https://opensource.org/licenses/MIT)
// ------------------------------------------------------------
-using System.Collections.Generic;
+using System;
+using System.Linq;
namespace SimpleOTP.Helpers
{
@@ -14,6 +15,7 @@ namespace SimpleOTP.Helpers
///
internal static class Base32Encoder
{
+ // Standard RFC 4648 Base32 alphabet
private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
///
@@ -23,16 +25,20 @@ namespace SimpleOTP.Helpers
/// Base32 string.
internal static string Encode(byte[] data)
{
- // FIXME: Encoder works correctly only with 160-bit keys
+ string binary = string.Empty;
+ foreach (byte b in data)
+ binary += Convert.ToString(b, 2).PadLeft(8, '0'); // Getting binary sequence to split into 5 digits
+
+ int numberOfBlocks = (binary.Length / 5) + Math.Clamp(binary.Length % 5, 0, 1);
+ string[] sequence = Enumerable.Range(0, numberOfBlocks)
+ .Select(i => binary.Substring(i * 5, Math.Min(5, binary.Length - (i * 5))).PadRight(5, '0'))
+ .ToArray(); // Splitting sequence on groups of 5
+
string output = string.Empty;
- for (int bitIndex = 0; bitIndex < data.Length * 8; bitIndex += 5)
- {
- int dualbyte = data[bitIndex / 8] << 8;
- if ((bitIndex / 8) + 1 < data.Length)
- dualbyte |= data[(bitIndex / 8) + 1];
- dualbyte = 0x1f & (dualbyte >> (16 - (bitIndex % 8) - 5));
- output += AllowedCharacters[dualbyte];
- }
+ foreach (string str in sequence)
+ output += AllowedCharacters[Convert.ToInt32(str, 2)];
+
+ output = output.PadRight(output.Length + (output.Length % 8), '=');
return output;
}
@@ -44,21 +50,14 @@ namespace SimpleOTP.Helpers
/// Initial byte array.
internal static byte[] Decode(string base32str)
{
- List output = new ();
- char[] bytes = base32str.ToCharArray();
- for (var bitIndex = 0; bitIndex < base32str.Length * 5; bitIndex += 8)
- {
- var dualbyte = AllowedCharacters.IndexOf(bytes[bitIndex / 5]) << 10;
- if ((bitIndex / 5) + 1 < bytes.Length)
- dualbyte |= AllowedCharacters.IndexOf(bytes[(bitIndex / 5) + 1]) << 5;
- if ((bitIndex / 5) + 2 < bytes.Length)
- dualbyte |= AllowedCharacters.IndexOf(bytes[(bitIndex / 5) + 2]);
+ base32str = base32str.Replace("=", string.Empty); // Removing padding
- dualbyte = 0xff & (dualbyte >> (15 - (bitIndex % 5) - 8));
- output.Add((byte)dualbyte);
- }
+ string[] quintets = base32str.Select(i => Convert.ToString(AllowedCharacters.IndexOf(i), 2).PadLeft(5, '0')).ToArray(); // Getting quintets
+ string binary = string.Join(null, quintets);
- return output.ToArray();
+ byte[] output = Enumerable.Range(0, binary.Length / 8).Select(i => Convert.ToByte(binary.Substring(i * 8, 8), 2)).ToArray();
+
+ return output;
}
}
}
\ No newline at end of file
diff --git a/SimpleOTP/Helpers/SecretGenerator.cs b/SimpleOTP/Helpers/SecretGenerator.cs
index 3ddc982..94a6501 100644
--- a/SimpleOTP/Helpers/SecretGenerator.cs
+++ b/SimpleOTP/Helpers/SecretGenerator.cs
@@ -20,7 +20,7 @@ namespace SimpleOTP.Helpers
/// Length of the key in bits
/// It should belong to [128-160] bit span
/// Default is: 160 bits.
- /// CURRENTLY THIS GENERATOR WORKS CORRECTLY ONLY WITH 160-BIT LENGTHS. Set at your own risk.
+ /// Number of bits will be rounded down to the nearest number which divides by 8.
/// Base32 encoded alphanumeric string with length form 16 to 20 characters.
public static string GenerateSecret(int length = 160)
{
diff --git a/SimpleOTP/SimpleOTP.csproj b/SimpleOTP/SimpleOTP.csproj
index d34c40c..93f655d 100644
--- a/SimpleOTP/SimpleOTP.csproj
+++ b/SimpleOTP/SimpleOTP.csproj
@@ -12,7 +12,7 @@
SimpleOTP
SimpleOTP
- 1.2.1
+ 1.2.2
.NET library for TOTP/HOTP implementation on server (ASP.NET) or client (Xamarin) side
Eugene Fox
FoxDev Studio
@@ -22,8 +22,8 @@
https://github.com/XFox111/SimpleOTP
en-US
otp;totp;dotnet;hotp;authenticator;2fa;mfa;security;oath
- - Fixed generated secret length
-- Fixed URI encoding issues in 'OTPConfiguration.GetUri()'
+ - Fixed Base32 encoder
+- Updated NuGet dependency packages