Skip to content

Instantly share code, notes, and snippets.

@ddjerqq
Last active November 13, 2023 17:15
Show Gist options
  • Save ddjerqq/af2c9a7d4d26c70a7e98c4e422f426eb to your computer and use it in GitHub Desktop.
Save ddjerqq/af2c9a7d4d26c70a7e98c4e422f426eb to your computer and use it in GitHub Desktop.
TOTP: Time-Based One-Time Password Algorithm Implementation in C#
using System.Security.Cryptography;
namespace Rfc6238;
public enum HmacAlgo
{
Sha1,
Sha256,
Sha512,
}
internal static class Extensions
{
internal static byte[] ComputeHash(this HmacAlgo algo, byte[] key, byte[] payload)
{
HMAC hmac = algo switch
{
HmacAlgo.Sha1 => new HMACSHA1(key),
HmacAlgo.Sha256 => new HMACSHA256(key),
HmacAlgo.Sha512 => new HMACSHA512(key),
_ => throw new ArgumentOutOfRangeException(nameof(algo), "Unknown HMAC algorithm"),
};
return hmac.ComputeHash(payload);
}
internal static byte[] ToBytes(this string hex)
{
if (hex.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
byte[] arr = new byte[hex.Length >> 1];
for (int i = 0; i < hex.Length >> 1; ++i)
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + GetHexVal(hex[(i << 1) + 1]));
return arr;
}
private static int GetHexVal(char hex)
{
// cast to int
int val = hex;
return val - (val < 58 ? 48 : val < 97 ? 55 : 87);
}
}
public static class Totp
{
public const long T0 = 0;
public const long X = 30;
public static string Generate(byte[] key, long timestamp, int digits, HmacAlgo crypto)
{
long T = (timestamp - T0) / X;
// convert to hex and pad with 16 0s
byte[] payload = T
.ToString("X016")
.ToBytes();
byte[] hash = crypto.ComputeHash(key, payload);
int offset = hash[^1] & 0xf;
int binary =
((hash[offset] & 0x7f) << 24)
| ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8)
| (hash[offset + 3] & 0xff);
int otp = binary % (int)Math.Pow(10, digits);
return otp.ToString($"D{digits}");
}
}
@ddjerqq
Copy link
Author

ddjerqq commented Nov 13, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment