RFC-6238 TOTP 实现不匹配示例

问题描述 投票:0回答:1

这是我对 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 的值(切换到大端)不会产生与示例匹配的输出,这就是我对这些行进行注释的原因。我确信时间的 ASCII 编码不是问题,因为 T 的十六进制值与示例匹配。我看到有些人建议秘密应该以 Base32 进行编码,但这似乎是为了将他们的实现与 Google Authenticator 相匹配,而不是实际的规范。

c# hash cryptography sha1 totp
1个回答
0
投票

感谢 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);
        }
    }

我还必须将密钥切换为大端。 我的代码现在符合 SHA1 规范 谢谢!

© www.soinside.com 2019 - 2024. All rights reserved.