using InABox.Clients;
using InABox.Core;
using InABox.Wpf;
using Microsoft.Exchange.WebServices.Data;
using Syncfusion.UI.Xaml.Controls.Barcode;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace PRSDesktop.Panels.Users
{
///
/// Interaction logic for QR2FAWindow.xaml
///
public partial class QR2FAWindow : ThemableWindow
{
private User User;
public QR2FAWindow(User user)
{
User = user;
user.AuthenticatorToken ??= User.GenerateAuthenticationToken();
InitializeComponent();
Barcode.QuietZone.All = 0;
SetBarcodeText(user.UserID, user.AuthenticatorToken);
Reset.IsEnabled = Security.CanEdit();
}
public void SetBarcodeText(string userID, byte[] key)
{
var secret = Base32Encoding.ToString(key).Trim('=');
var info = new Client().Load().FirstOrDefault();
if (info == null)
info = new CompanyInformation();
SetupKey.Text = secret;
Barcode.Text = $"otpauth://totp/PRS-{Uri.EscapeDataString(userID)}?secret={secret}&issuer=PRS%20-%20{Uri.EscapeDataString(info.CompanyName)}";
}
private void OK_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void Reset_Click(object sender, RoutedEventArgs e)
{
if(MessageBox.Show("Doing this will invalidate the authentication of this user, preventing them from being able to log in until they have rescanned this code. Do you wish to proceed?", "Continue?", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
User.AuthenticatorToken = User.GenerateAuthenticationToken();
SetBarcodeText(User.UserID, User.AuthenticatorToken);
}
}
}
// Code taken from https://stackoverflow.com/questions/641361/base32-decoding
public class Base32Encoding
{
public static byte[] ToBytes(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}
input = input.TrimEnd('='); //remove padding characters
int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
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 we didn't end with a full byte
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 we didn't end with a full char
if (arrayIndex != charCount)
{
returnArray[arrayIndex++] = ValueToChar(nextChar);
while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
}
return new string(returnArray);
}
private static int CharToValue(char c)
{
int value = (int)c;
//65-90 == uppercase letters
if (value < 91 && value > 64)
{
return value - 65;
}
//50-55 == numbers 2-7
if (value < 56 && value > 49)
{
return value - 24;
}
//97-122 == lowercase letters
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");
}
}
}