添加Rfc6238AuthenticationService的字符数和延长时间

时间:2017-05-02 19:39:25

标签: c# asp.net security one-time-password

我在https://github.com/aspnet/Identity/blob/85012bd0ac83548f7eab31f0585dae3836935d9d/src/Microsoft.AspNet.Identity/Rfc6238AuthenticationService.cs

使用Rfc6238AuthenticationService

使用rfc6238 https://tools.ietf.org/html/rfc6238

internal static class Rfc6238AuthenticationService
{
    private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly TimeSpan _timestep = TimeSpan.FromMinutes(3);
    private static readonly Encoding _encoding = new UTF8Encoding(false, true);

    private static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
    {
        // # of 0's = length of pin
        const int Mod = 1000000;

        // See https://tools.ietf.org/html/rfc4226
        // We can add an optional modifier
        var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber));
        var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier));

        // Generate DT string
        var offset = hash[hash.Length - 1] & 0xf;
        Debug.Assert(offset + 4 < hash.Length);
        var binaryCode = (hash[offset] & 0x7f) << 24
                         | (hash[offset + 1] & 0xff) << 16
                         | (hash[offset + 2] & 0xff) << 8
                         | (hash[offset + 3] & 0xff);

        return binaryCode % Mod;
    }

    private static byte[] ApplyModifier(byte[] input, string modifier)
    {
        if (String.IsNullOrEmpty(modifier))
        {
            return input;
        }

        var modifierBytes = _encoding.GetBytes(modifier);
        var combined = new byte[checked(input.Length + modifierBytes.Length)];
        Buffer.BlockCopy(input, 0, combined, 0, input.Length);
        Buffer.BlockCopy(modifierBytes, 0, combined, input.Length, modifierBytes.Length);
        return combined;
    }

    // More info: https://tools.ietf.org/html/rfc6238#section-4
    private static ulong GetCurrentTimeStepNumber()
    {
        var delta = DateTime.UtcNow - _unixEpoch;
        return (ulong)(delta.Ticks / _timestep.Ticks);
    }

    public static int GenerateCode(byte[] securityToken, string modifier = null)
    {
        if (securityToken == null)
        {
            throw new ArgumentNullException(nameof(securityToken));
        }

        // Allow a variance of no greater than 90 seconds in either direction
        var currentTimeStep = GetCurrentTimeStepNumber();
        using (var hashAlgorithm = new HMACSHA1(securityToken))
        {
            return ComputeTotp(hashAlgorithm, currentTimeStep, modifier);
        }
    }

    public static bool ValidateCode(byte[] securityToken, int code, string modifier = null)
    {
        if (securityToken == null)
        {
            throw new ArgumentNullException(nameof(securityToken));
        }

        // Allow a variance of no greater than 90 seconds in either direction
        var currentTimeStep = GetCurrentTimeStepNumber();
        using (var hashAlgorithm = new HMACSHA1(securityToken))
        {
            for (var i = -2; i <= 2; i++)
            {
                var computedTotp = ComputeTotp(hashAlgorithm, (ulong)((long)currentTimeStep + i), modifier);
                if (computedTotp == code)
                {
                    return true;
                }
            }
        }

        // No match
        return false;
    }
}

是否可以在此类中添加字符限制并使其可配置(如6个字符)?此外,是否可以延长令牌的时间并使其可配置(如120秒)?

1 个答案:

答案 0 :(得分:1)

这是OTP被截断为定义长度的地方:

return binaryCode % Mod;

因此,您只需将Mod的值更改为1000000即可获得6位代码。

负责令牌时间的变量是 timestepNumber 。因此,如果需要,可以更改其计算中的逻辑。