Currently, I am trying to implement the TOTP standard by referring to RFC 4226 (HOTP) and RFC 6238 (TOTP). My HOTP and TOTP implementations have already passed the tests provided in the standards. The key used is 12345678901234567890
.
So, I searched for an online TOTP testing website, https://authenticationtest.com/totpChallenge/, to test the usability of my TOTP generation code.
I compared the results of my code with the passwords generated by Google Authenticator on my phone and found that the results were not as expected.
Here is my code. As this is my first attempt at implementing this standard, there might be some oversights. Currently, I have no idea how to test this code.
using System.Security.Cryptography;
using System.Text;
namespace ConsoleTotp;
class HOTP
{
private byte[] Secret { get; set; }
private byte[] Counter { get; set; }
private int Digit { get; set; }
public HOTP(byte[] secret, int counter, int digit)
{
Secret = secret;
Digit = digit;
var bytes = new byte[8];
for (var i = 7; i >= 0; i--)
{
bytes[i] = (byte)(counter & 0xFF);
counter >>= 8;
}
Counter = bytes;
}
public string Code()
{
var hmac = new HMACSHA1(Secret);
var hs = hmac.ComputeHash(Counter);
var hsString = BytesConvertToString(hs);
var code = (Dt(hsString) % (int)Math.Pow(10, Digit)).ToString().PadLeft(Digit, '0');
return code;
}
private int Dt(string p)
{
var offset = p.Last() & 0x0F;
return ((p[offset] & 0x7F) << 24) |
((p[offset + 1] & 0xFF) << 16) |
((p[offset + 2] & 0xFF) << 8) |
(p[offset + 3] & 0xFF);
}
private string BytesConvertToString(byte[] data)
{
var charaters = data.Select(x => (char)x).ToArray();
return new string(charaters);
}
}
public class TOTP
{
private byte[] Secret;
private int Timestep = 30;
private int Digit = 6;
public TOTP(byte[] secret)
{
Secret = secret;
}
public TOTP(byte[] secret, int timestep)
{
Secret = secret;
Timestep = timestep;
}
public TOTP(byte[] secret, int timestep, int digit)
{
Secret = secret;
Timestep = timestep;
Digit = digit;
}
public string Code()
{
var testTime = new DateTime(2603, 10, 11, 11, 33, 20);
var t = (int) (Math.Floor(DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds / Timestep));
var hotp = new HOTP(Secret, t, Digit);
var code = hotp.Code();
return code;
}
}
class Program
{
public static void Main()
{
var secret = Encoding.ASCII.GetBytes("I65VU7K5ZQL7WB4E");
var totp = new TOTP(secret, 30, 6);
var code = totp.Code();
Console.WriteLine(code); // Wrong result
}
}
Thank you sincerely for the help from each and every one of you.
Referenced the following resources:
https://www.rfc-editor.org/rfc/rfc4226
https://www.rfc-editor.org/rfc/rfc6238.html
https://medium.com/zeals-tech-blog/implementing-your-own-time-based-otp-generator-3b971a31330b
https://github.com/freeotp/freeotp-ios
My expectation is that this code can generate the correct TOTP codes.