public class QR
{
public static byte[] ToBytes(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}
input = input.TrimEnd('=');
int byteCount = input.Length * 5 / 8;
byte[] returnArray = new byte[byteCount];
byte curByte = 0, bitsRemaining = 8;
int mask = 0, arrayIndex = 0;
foreach (char c in input)
{
int cValue = CharToValue(c);
if (bitsRemaining > 5)
{
mask = cValue << (bitsRemaining - 5);
curByte = (byte)(curByte | mask);
bitsRemaining -= 5;
}
else
{
mask = cValue >> (5 - bitsRemaining);
curByte = (byte)(curByte | mask);
returnArray[arrayIndex++] = curByte;
curByte = (byte)(cValue << (3 + bitsRemaining));
bitsRemaining += 3;
}
}
if (arrayIndex != byteCount) {
returnArray[arrayIndex] = curByte;
}
return returnArray;
}
public static string ToString(byte[] input)
{
if (input == null || input.Length == 0) {
throw new ArgumentNullException("input");
}
int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
char[] returnArray = new char[charCount];
byte nextChar = 0, bitsRemaining = 5;
int arrayIndex = 0;
foreach (byte b in input)
{
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
returnArray[arrayIndex++] = ValueToChar(nextChar);
if (bitsRemaining < 4)
{
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
returnArray[arrayIndex++] = ValueToChar(nextChar);
bitsRemaining += 5;
}
bitsRemaining -= 3;
nextChar = (byte)((b << bitsRemaining) & 31);
}
if (arrayIndex != charCount)
{
returnArray[arrayIndex++] = ValueToChar(nextChar);
while (arrayIndex != charCount) returnArray[arrayIndex++] = '=';
}
return new string(returnArray);
}
private static int CharToValue(char c)
{
int value = (int)c;
if (value < 91 && value > 64) {
return value - 65;
}
if (value < 56 && value > 49) {
return value - 24;
}
if (value < 123 && value > 96) {
return value - 97;
}
throw new ArgumentException("Character is not a Base32 character.", "c");
}
private static char ValueToChar(byte b)
{
if (b < 26) {
return (char)(b + 65);
}
if (b < 32) {
return (char)(b + 24);
}
throw new ArgumentException("Byte is not a value Base32 value.", "b");
}
const int IntervalLength = 30;
const int PinLength = 6;
static readonly int PinModulo = (int)Math.Pow(10, PinLength);
static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public string provisionUrl;
private byte[] key;
public string GoogleAuthControl(string url)
{
try
{ key = ToBytes(url.Split('=')[1]);
GeneratePin();
}
catch {
}
return GeneratePin();
}
static long CurrentInterval
{
get {
var ElapsedSeconds = (long)Math.Floor((DateTime.UtcNow - UnixEpoch).TotalSeconds);
return ElapsedSeconds / IntervalLength;
}
}
public string GeneratePin() {
return GeneratePin(this.key, CurrentInterval);
}
static string GeneratePin(byte[] key, long counter)
{
const int SizeOfInt32 = 4;
var CounterBytes = BitConverter.GetBytes(counter);
if (BitConverter.IsLittleEndian) {
Array.Reverse(CounterBytes);
}
var Hash = new HMACSHA1(key).ComputeHash(CounterBytes);
var Offset = Hash[Hash.Length - 1] & 0xF;
var SelectedBytes = new byte[SizeOfInt32];
Buffer.BlockCopy(Hash, Offset, SelectedBytes, 0, SizeOfInt32);
if (BitConverter.IsLittleEndian) {
Array.Reverse(SelectedBytes);
}
var SelectedInteger = BitConverter.ToInt32(SelectedBytes, 0);
var TruncatedHash = SelectedInteger & 0x7FFFFFFF;
var Pin = TruncatedHash % PinModulo;
return Pin.ToString(CultureInfo.InvariantCulture).PadLeft(PinLength, '0');
}
}