Skip to content

Commit

Permalink
add auth func
Browse files Browse the repository at this point in the history
  • Loading branch information
gosha20777 committed Oct 26, 2019
1 parent c2b8ad1 commit f233cd0
Show file tree
Hide file tree
Showing 12 changed files with 426 additions and 6 deletions.
3 changes: 2 additions & 1 deletion RescuerLaApp/Models/AppStatusInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ private ISolidColorBrush GetColor()
case Status.Ready: return new SolidColorBrush(Color.FromRgb(0, 128, 255));
case Status.Success: return new SolidColorBrush(Color.FromRgb(0, 135, 60));
case Status.Working: return new SolidColorBrush(Color.FromRgb(226, 90, 0));
case Status.Error: return new SolidColorBrush(Color.FromRgb(216, 14, 0));
case Status.Error: return new SolidColorBrush(Color.FromRgb(216, 14, 0));
case Status.Unauthenticated: return new SolidColorBrush(Color.FromRgb(120, 0, 120));
default: throw new Exception($"Invalid app status {_status.ToString()}");
}
}
Expand Down
1 change: 1 addition & 0 deletions RescuerLaApp/Models/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum Status
Ready,
Working,
Success,
Unauthenticated,
Error
}

Expand Down
83 changes: 83 additions & 0 deletions RescuerLaApp/Models/PasswordHasher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;

namespace RescuerLaApp.Models
{
public class PasswordHasher
{
public string GenerateIdentityV3Hash(string password, KeyDerivationPrf prf = KeyDerivationPrf.HMACSHA256, int iterationCount = 10000, int saltSize = 16)
{
using (var rng = RandomNumberGenerator.Create()){
var salt = new byte[saltSize];
rng.GetBytes(salt);

var pbkdf2Hash = KeyDerivation.Pbkdf2(password, salt, prf, iterationCount, 32);
return Convert.ToBase64String(ComposeIdentityV3Hash(salt, (uint)iterationCount, pbkdf2Hash));
}
}
public bool VerifyIdentityV3Hash(string password, string passwordHash)
{
var identityV3HashArray = Convert.FromBase64String(passwordHash);
if (identityV3HashArray[0] != 1) throw new InvalidOperationException("passwordHash is not Identity V3");

var prfAsArray = new byte[4];
Buffer.BlockCopy(identityV3HashArray, 1, prfAsArray, 0, 4);
var prf = (KeyDerivationPrf)ConvertFromNetworOrder(prfAsArray);

var iterationCountAsArray = new byte[4];
Buffer.BlockCopy(identityV3HashArray, 5, iterationCountAsArray, 0, 4);
var iterationCount = (int)ConvertFromNetworOrder(iterationCountAsArray);

var saltSizeAsArray = new byte[4];
Buffer.BlockCopy(identityV3HashArray, 9, saltSizeAsArray, 0, 4);
var saltSize = (int)ConvertFromNetworOrder(saltSizeAsArray);

var salt = new byte[saltSize];
Buffer.BlockCopy(identityV3HashArray, 13, salt, 0, saltSize);

var savedHashedPassword = new byte[identityV3HashArray.Length - 1 - 4 - 4 - 4 - saltSize];
Buffer.BlockCopy(identityV3HashArray, 13 + saltSize, savedHashedPassword, 0, savedHashedPassword.Length);

var hashFromInputPassword = KeyDerivation.Pbkdf2(password, salt, prf, iterationCount, 32);

return AreByteArraysEqual(hashFromInputPassword, savedHashedPassword);
}
private byte[] ComposeIdentityV3Hash(byte[] salt, uint iterationCount, byte[] passwordHash)
{
var hash = new byte[1 + 4/*KeyDerivationPrf value*/ + 4/*Iteration count*/ + 4/*salt size*/ + salt.Length /*salt*/ + 32 /*password hash size*/];
hash[0] = 1; //Identity V3 marker

Buffer.BlockCopy(ConvertToNetworkOrder((uint)KeyDerivationPrf.HMACSHA256), 0, hash, 1, sizeof(uint));
Buffer.BlockCopy(ConvertToNetworkOrder((uint)iterationCount), 0, hash, 1 + sizeof(uint), sizeof(uint));
Buffer.BlockCopy(ConvertToNetworkOrder((uint)salt.Length), 0, hash, 1 + 2 * sizeof(uint), sizeof(uint));
Buffer.BlockCopy(salt, 0, hash, 1 + 3 * sizeof(uint), salt.Length);
Buffer.BlockCopy(passwordHash, 0, hash, 1 + 3 * sizeof(uint) + salt.Length, passwordHash.Length);

return hash;
}

private bool AreByteArraysEqual(byte[] array1, byte[] array2)
{
if (array1.Length != array2.Length) return false;

var areEqual = true;
for (var i = 0; i < array1.Length; i++)
{
areEqual &= (array1[i] == array2[i]);
}
return areEqual;
}

private byte[] ConvertToNetworkOrder(uint number)
{
return BitConverter.GetBytes(number).Reverse().ToArray();
}

private uint ConvertFromNetworOrder(byte[] reversedUint)
{
return BitConverter.ToUInt32(reversedUint.Reverse().ToArray(), 0);
}
}
}
2 changes: 1 addition & 1 deletion RescuerLaApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal static class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Lacmus desktop application. Version 0.3.0 alpha. \nCopyright (c) 2019 Georgy Perevozghikov <[email protected]>\nGithub page: https://github.com/lizaalert/lacmus/.\nProvided by Yandex Cloud: https://cloud.yandex.com/.");
Console.WriteLine("Lacmus desktop application. Version 0.3.1 alpha. \nCopyright (c) 2019 Georgy Perevozghikov <[email protected]>\nGithub page: https://github.com/lizaalert/lacmus/.\nProvided by Yandex Cloud: https://cloud.yandex.com/.");
Console.WriteLine("This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.");
Console.WriteLine("This is free software, and you are welcome to redistribute it\nunder certain conditions; type `show c' for details.");
Console.WriteLine("------------------------------------");
Expand Down
1 change: 1 addition & 0 deletions RescuerLaApp/RescuerLaApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<PackageReference Include="Docker.DotNet" Version="3.125.2" />
<PackageReference Include="MessageBox.Avalonia" Version="0.8.3" />
<PackageReference Include="MetadataExtractor" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="3.0.0" />
<PackageReference Include="NewtonSoft.Json" Version="12.0.2" />
<PackageReference Include="ReactiveUI.Fody" Version="9.16.3" />
</ItemGroup>
Expand Down
30 changes: 26 additions & 4 deletions RescuerLaApp/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Newtonsoft.Json;
using RescuerLaApp.Views;
using Directory = System.IO.Directory;

namespace RescuerLaApp.ViewModels
Expand Down Expand Up @@ -48,7 +49,11 @@ public MainWindowViewModel()

var canExecute = this
.WhenAnyValue(x => x.Status)
.Select(status => status.Status != Enums.Status.Working);
.Select(status => status.Status != Enums.Status.Working && status.Status != Enums.Status.Unauthenticated);

var canAuth = this
.WhenAnyValue(x => x.Status)
.Select(status => status.Status == Enums.Status.Unauthenticated);

var canShowPedestrians = this
.WhenAnyValue(x => x._frames)
Expand All @@ -75,7 +80,9 @@ public MainWindowViewModel()
ShowGeoDataCommand = ReactiveCommand.Create(ShowGeoData, canExecute);
HelpCommand = ReactiveCommand.Create(Help);
AboutCommand = ReactiveCommand.Create(About);
ExitCommand = ReactiveCommand.Create(Exit, canExecute);
SignUpCommand = ReactiveCommand.Create(SignUp, canAuth);
SignInCommand = ReactiveCommand.Create(SignIn, canAuth);
ExitCommand = ReactiveCommand.Create(Exit);
}

public void UpdateFramesRepo()
Expand Down Expand Up @@ -106,7 +113,7 @@ public void UpdateFramesRepo()

[Reactive] public List<Frame> Frames { get; set; } = new List<Frame>();

[Reactive] public AppStatusInfo Status { get; set; } = new AppStatusInfo { Status = Enums.Status.Ready };
[Reactive] public AppStatusInfo Status { get; set; } = new AppStatusInfo { Status = Enums.Status.Unauthenticated };

[Reactive] public ImageBrush ImageBrush { get; set; } = new ImageBrush { Stretch = Stretch.Uniform };

Expand Down Expand Up @@ -140,6 +147,8 @@ public void UpdateFramesRepo()
public ReactiveCommand<Unit, Unit> ShowGeoDataCommand { get; }
public ReactiveCommand<Unit, Unit> HelpCommand { get; }
public ReactiveCommand<Unit, Unit> AboutCommand { get; }
public ReactiveCommand<Unit, Unit> SignUpCommand { get; }
public ReactiveCommand<Unit, Unit> SignInCommand { get; }
public ReactiveCommand<Unit, Unit> ExitCommand { get; }


Expand Down Expand Up @@ -625,7 +634,7 @@ public async void About()
new ButtonDefinition{Name = "Github"}
},
ContentTitle = "About",
ContentHeader = "Lacmus desktop application. Version 0.3.0 alpha.",
ContentHeader = "Lacmus desktop application. Version 0.3.1 alpha.",
ContentMessage = message,
Icon = Icon.Avalonia,
Style = Style.None,
Expand All @@ -640,6 +649,19 @@ public async void About()
case "github": OpenUrl("https://github.com/lizaalert/lacmus"); break;
}
}

public async void SignUp()
{
var result = await Views.SignUpWindow.Show(null);
if(Status.Status == Enums.Status.Unauthenticated && result.IsSignIn)
Status = new AppStatusInfo() {Status = Enums.Status.Ready};
}
public async void SignIn()
{
var result = await Views.SignInWindow.Show(null);
if(Status.Status == Enums.Status.Unauthenticated && result.IsSignIn)
Status = new AppStatusInfo() {Status = Enums.Status.Ready};
}

public async void Exit()
{
Expand Down
4 changes: 4 additions & 0 deletions RescuerLaApp/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
<MenuItem Name="mFileSave" Header="Save all" Command="{Binding SaveAllCommand}"/>
<MenuItem Name="mFileSaveAllImagesWithObjects" Header="Save only images with pedestrians" Command="{Binding SaveAllImagesWithObjectsCommand}"/>
<Separator></Separator>
<MenuItem Name="mSIgnIn" Header="Sign in" Command="{Binding SignInCommand}"/>
<MenuItem Name="mSignUp" Header="Sign up" Command="{Binding SignUpCommand}"/>
<Separator></Separator>
<MenuItem Name="mFileExit" Header="Exit" Command="{Binding ExitCommand}"/>
</MenuItem>
<MenuItem Name="mModel" Header="Model">
Expand All @@ -49,6 +52,7 @@
</Menu>
<ToolTip>
<StackPanel Orientation="Horizontal">
<Button Name="bSignIn" Command="{Binding SignInCommand}">Suin In</Button>
<Button Name="bOpen" Command="{Binding OpenFileCommand}">Open</Button>
<Button Name="bPredict" Command="{Binding PredictAllCommand}">Predict all</Button>
<Separator/>
Expand Down
20 changes: 20 additions & 0 deletions RescuerLaApp/Views/SignInWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RescuerLaApp.Views.SignInWindow" SizeToContent="WidthAndHeight" CanResize="False">
<StackPanel HorizontalAlignment="Center">
<TextBlock Margin="10 2 10 2">Email</TextBlock>
<TextBox Name="tbEmail" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<TextBlock Margin="10 2 10 2">Password</TextBlock>
<TextBox Name="tbPassword" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Name="Buttons">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="10, 5, 10, 5"/>
</Style>
</StackPanel.Styles>
</StackPanel>
</StackPanel>
</Window>
113 changes: 113 additions & 0 deletions RescuerLaApp/Views/SignInWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Data;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using MessageBox.Avalonia.DTO;
using MessageBox.Avalonia.Enums;
using Newtonsoft.Json;
using RescuerLaApp.Models;

namespace RescuerLaApp.Views
{
class SignInWindow : Window
{
[JsonObject]
public class SignInResult
{
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("passwordHash")]
public string PasswordHash { get; set; }
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("time")]
public string Time { get; set; }

public bool IsSignIn { get; set; } = false;
}

public SignInWindow()
{
AvaloniaXamlLoader.Load(this);
}

public static Task<SignInResult> Show(Window parent)
{
var title = "Sign In";
var msgbox = new SignInWindow()
{
Title = title
};
var buttonPanel = msgbox.FindControl<StackPanel>("Buttons");

SignInResult res = new SignInResult();

void AddButton(string caption)
{
var btn = new Button {Content = caption};
btn.Click += (_, __) =>
{
var patch = AppDomain.CurrentDomain.BaseDirectory + "user_info";
if (File.Exists(patch))
{
res = JsonConvert.DeserializeObject<SignInResult>(File.ReadAllText(patch));
}
else
{
ShowError("There are no account. Please sign up");
return;
}

var passwordHash = msgbox.FindControl<TextBox>("tbPassword").Text;
var email = msgbox.FindControl<TextBox>("tbEmail").Text;

if (string.IsNullOrWhiteSpace(passwordHash) || passwordHash.Length < 6)
{
ShowError("Incorrect Password");
return;
}
PasswordHasher hasher = new PasswordHasher();
if (string.IsNullOrWhiteSpace(passwordHash) || !hasher.VerifyIdentityV3Hash(passwordHash, res.PasswordHash))
{
ShowError("Incorrect Password");
return;
}
if (string.IsNullOrWhiteSpace(email) || !email.Contains('@') || !email.Contains('.') || email != res.Email)
{
ShowError("Incorrect Email");
return;
}

res.IsSignIn = true;
msgbox.Close();
};
buttonPanel.Children.Add(btn);
}

AddButton("Sign Ip");
var tcs = new TaskCompletionSource<SignInResult>();
msgbox.Closed += delegate { tcs.TrySetResult(res); };
if (parent != null)
msgbox.ShowDialog(parent);
else msgbox.Show();
return tcs.Task;
}

private static async void ShowError(string message)
{
var msgbox = new MessageBox.Avalonia.MessageBoxWindow(new MessageBoxParams
{
Button = ButtonEnum.Ok,
ContentTitle = "Error",
ContentMessage = message,
Icon = MessageBox.Avalonia.Enums.Icon.Error,
Style = Style.None,
ShowInCenter = true
});
var result = await msgbox.Show();
}
}
}
Empty file.
26 changes: 26 additions & 0 deletions RescuerLaApp/Views/SignUpWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RescuerLaApp.Views.SignUpWindow" SizeToContent="WidthAndHeight" CanResize="False">
<StackPanel HorizontalAlignment="Center">
<TextBlock Margin="10 2 10 2">Email</TextBlock>
<TextBox Name="tbEmail" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<TextBlock Margin="10 2 10 2">Nick name</TextBlock>
<TextBox Name="tbNickName" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<TextBlock Margin="10 2 10 2">First name</TextBlock>
<TextBox Name="tbFirstName" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<TextBlock Margin="10 2 10 2">Last name</TextBlock>
<TextBox Name="tbLastName" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<TextBlock Margin="10 2 10 2">Password</TextBlock>
<TextBox Name="tbPassword" Margin="10 2 10 2" Height="30" Width="300"></TextBox>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Name="Buttons">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="10, 5, 10, 5"/>
</Style>
</StackPanel.Styles>
</StackPanel>
</StackPanel>
</Window>
Loading

0 comments on commit f233cd0

Please sign in to comment.