Last active
May 30, 2023 17:43
-
-
Save marcomagdy/6bd4d8dc41e77b37dc74 to your computer and use it in GitHub Desktop.
Google MFA in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Program | |
{ | |
static void Main(string[] args) { | |
var secretByets = Base32Encoding.ToBytes("supersecretpassword"); | |
var input = GetEpoch() / 30; | |
var hmac = new HMACSHA1(secretByets); | |
var output = hmac.ComputeHash(toBytes(input)); | |
Console.WriteLine("{0:d6}", calculate(output)); | |
// print value for the past 30 seconds as well for user convenience | |
output = hmac.ComputeHash(toBytes(--input)); | |
Console.WriteLine("{0:d6}", calculate(output)); | |
} | |
private static uint calculate(byte[] output) { | |
// Slim down the value from 20 bytes to 4 bytes. | |
// But, instead of always using the first 4 bytes, | |
// use the value of last byte of the 20-bytes | |
// to determine the start index of the 4-bytes we're interested in. | |
var index = output[output.Length - 1] & 0x0f; | |
var slimmed = new byte[4]; | |
slimmed[0] = output[index++]; | |
slimmed[1] = output[index++]; | |
slimmed[2] = output[index++]; | |
slimmed[3] = output[index]; | |
slimmed[0] &= 0x7f; // ignore the most significant bit as per RFC 4226 | |
var large = ToUint32(slimmed); | |
// modulus 1 million to ensure the values are 6 digits or less | |
return large % 1000000; | |
} | |
// convert to/from byte arrays to/from uint using Big-Endian | |
// BitConverter class is will use Little-Endian on Windows | |
private static byte[] toBytes(UInt64 input) { | |
var result = new byte[8]; | |
var shifts = new UInt16[] {56, 48, 40, 32, 24, 16, 8, 0}; | |
for (int i = 0; i < shifts.Length; i++) { | |
result[i] = (byte)((input >> shifts[i]) & 0xFF); | |
} | |
return result; | |
} | |
private static UInt32 ToUint32(byte[] bytes) { | |
return (UInt32)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | (bytes[3])); | |
} | |
private static UInt64 GetEpoch() { | |
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |
return (UInt64)((DateTime.UtcNow - epoch).TotalSeconds); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment