Google MFA in C#
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