提问人:birb 提问时间:10/11/2023 最后编辑:birb 更新时间:10/11/2023 访问量:70
RFC-6238 TOTP 实现与示例不匹配
RFC-6238 TOTP implementation not matching examples
问:
这是我对 RFC 6238 的简单实现
using System.Text;
using System.Security.Cryptography;
namespace totp;
class Program
{
static void Main(string[] args)
{
//string secret = "12345678901234567890"; // HOTP
string secret = "3132333435363738393031323334353637383930"; // TOTP
while (true)
{
long unix = 1111111109;
long unixRoundedRaw = (long)Math.Floor(unix / 30.0);
long unixRounded = (long)Math.Floor(unix / 30.0) * 30;
// unix -= unix % 30;
Console.WriteLine("unix: " + unix);
Console.WriteLine("time: " + DateTimeOffset.FromUnixTimeSeconds(unix).DateTime);
Console.WriteLine("tron: " + DateTimeOffset.FromUnixTimeSeconds(unixRounded).DateTime);
Console.WriteLine("totp: " + generateTOTP(secret, unixRoundedRaw, 6));
Console.WriteLine("~~~~~~~~~~~~~~~~~~\n");
Thread.Sleep(1000);
}
}
static string generateTOTP(string secret, long time, int digits)
{
string result = "";
// while (time.Length < 16) { time = "0" + time; }
//byte[] msg = { 0, 0, 0, 0, 0, 0, 0, 2 }; // HOTP
byte[] msg = BitConverter.GetBytes(time); // TOTP
//if (BitConverter.IsLittleEndian) { Array.Reverse(msg); }
Console.WriteLine("hext: " + BitConverter.ToString(msg).Replace("-", ""));
byte[] key = Encoding.ASCII.GetBytes(secret);
//if (BitConverter.IsLittleEndian) { Array.Reverse(key); }
HMACSHA1 hmac_sha = new HMACSHA1(key);
byte[] hash = hmac_sha.ComputeHash(msg);
//if (BitConverter.IsLittleEndian) { Array.Reverse(hash); }
Console.WriteLine("hash: " + BitConverter.ToString(hash).Replace("-", ""));
int offset = hash[^1] & 0x0f;
int bin_code = (hash[offset] & 0x7f) << 24
| (hash[offset + 1] & 0xff) << 16
| (hash[offset + 2] & 0xff) << 8
| (hash[offset + 3] & 0xff);
result = (bin_code % Math.Pow(10, digits)).ToString();
while (result.Length < digits) { result = "0" + result; }
return result;
}
}
我遗漏了一些注释行,这些行用于针对 HOTP (RFC 4226) 进行测试,这些行可以正常工作。输出的数字与规格表中的示例匹配:
从RFC4266发挥:
Truncated
Count Hexadecimal Decimal HOTP
0 4c93cf18 1284755224 755224
1 41397eea 1094287082 287082
2 82fef30 137359152 359152
我的代码:
hext: 0000000000000002
hash: 0BACB7FA082FEF30782211938BC1C5E70416FF44
totp: 359152
但是,当运行我的代码生成 TOTP 代码时,时间匹配,并且 T 的十六进制值匹配,但 TOTP 不正确。
发挥RFC6238
+-------------+--------------+------------------+----------+--------+
| Time (sec) | UTC Time | Value of T (hex) | TOTP | Mode |
+-------------+--------------+------------------+----------+--------+
| 59 | 1970-01-01 | 0000000000000001 | 94287082 | SHA1 |
| | 00:00:59 | | | |
| 1111111109 | 2005-03-18 | 00000000023523EC | 07081804 | SHA1 |
| | 01:58:29 | | | |
我的代码:
unix: 1111111109
time: 18/03/2005 01:58:29
tron: 18/03/2005 01:58:00
hext: EC23350200000000
hash: 0AE6677BAD3CD817B337F34E37AEC0D12EED7CC4
totp: 962199
~~~~~~~~~~~~~~~~~~
反转 T 的值(切换到 Big endian)不会产生与示例匹配的输出,这就是我将这些行保留注释的原因。我确信时间的 ASCII 编码不是问题,因为 T 的十六进制值与示例匹配。我看到有些人建议密钥应该用 Base32 编码,但这似乎是为了将他们的实现与 Google Authenticator 相匹配,而不是实际的规范。
答:
0赞
birb
10/11/2023
#1
感谢 Topaco 的建议 - 我遍历了每个中间变量,发现我的秘密正在被编码,而不是作为字节的文字数组。使用此函数解析此问题解决了以下问题:
static public IEnumerable<byte> GetBytesFromByteString(string s)
{
for (int index = 0; index < s.Length; index += 2)
{
yield return Convert.ToByte(s.Substring(index, 2), 16);
}
}
我还必须将密钥切换到 Big endian。 我的代码现在符合 SHA1 的规范 谢谢!
评论