rework to Azaion.Suite

This commit is contained in:
Alex Bezdieniezhnykh
2024-11-21 13:41:32 +02:00
parent 2cf69f4e4e
commit 5a592e9dbf
76 changed files with 1739 additions and 882 deletions
+7
View File
@@ -0,0 +1,7 @@
<Application x:Class="Azaion.Suite.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
+114
View File
@@ -0,0 +1,114 @@
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Azaion.Annotator;
using Azaion.Annotator.DTO;
using Azaion.Annotator.Extensions;
using Azaion.Common.DTO;
using Azaion.Common.DTO.Config;
using Azaion.Common.Extensions;
using Azaion.Suite.Services;
using Azaion.Suite.Services.DTO;
using Azaion.Dataset;
using LibVLCSharp.Shared;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Serilog;
namespace Azaion.Suite;
public partial class App : Application
{
private readonly IHost _host;
private readonly ILogger<App> _logger;
private readonly IMediator _mediator;
public App()
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File(
path: "Logs/log.txt",
rollingInterval: RollingInterval.Day)
.CreateLogger();
_host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, config) => config
.AddCommandLine(Environment.GetCommandLineArgs())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true))
.ConfigureServices((context, services) =>
{
services.AddSingleton<Loader>();
services.AddSingleton<IHardwareService, HardwareService>();
services.AddSingleton<IResourceLoader, ResourceLoader>();
services.Configure<ApiConfig>(context.Configuration.GetSection(nameof(ApiConfig)));
services.AddSingleton<IConfigUpdater, ConfigUpdater>();
services.AddSingleton<Annotator.Annotator>();
services.AddSingleton<DatasetExplorer>();
services.AddSingleton<HelpWindow>();
services.AddSingleton<IAIDetector, YOLODetector>();
services.AddMediatR(c => c.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
services.AddSingleton<LibVLC>(_ => new LibVLC());
services.AddSingleton<FormState>();
services.AddSingleton<MediaPlayer>(sp =>
{
var libVLC = sp.GetRequiredService<LibVLC>();
return new MediaPlayer(libVLC);
});
services.AddSingleton<AnnotatorEventHandler>();
services.AddSingleton<VLCFrameExtractor>();
services.AddHttpClient<AzaionApiClient>((sp, client) =>
{
var apiConfig = sp.GetRequiredService<IOptions<ApiConfig>>().Value;
client.BaseAddress = new Uri(apiConfig.Url);
client.Timeout = TimeSpan.FromSeconds(apiConfig.TimeoutSeconds);
});
services.AddSingleton<DatasetExplorer>();
services.AddSingleton<IGalleryManager, GalleryManager>();
})
.Build();
_mediator = _host.Services.GetRequiredService<IMediator>();
_logger = _host.Services.GetRequiredService<ILogger<App>>();
DispatcherUnhandledException += OnDispatcherUnhandledException;
}
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
_logger.LogError(e.Exception, e.Exception.Message);
e.Handled = true;
}
protected override async void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(UIElement), UIElement.KeyDownEvent, new RoutedEventHandler(GlobalClick));
await _host.StartAsync();
_host.Services.GetRequiredService<Loader>().Show();
base.OnStartup(e);
}
private void GlobalClick(object sender, RoutedEventArgs e)
{
var args = (KeyEventArgs)e;
var keyEvent = new KeyEvent(sender, args, (sender as FrameworkElement).GetParentWindow());
_ = ThrottleExt.Throttle(() => _mediator.Publish(keyEvent), TimeSpan.FromMilliseconds(50));
}
protected override async void OnExit(ExitEventArgs e)
{
await _host.StopAsync();
_host.Dispose();
base.OnExit(e);
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
+37
View File
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ApplicationIcon>..\logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Azaion.Annotator\Azaion.Annotator.csproj" />
<ProjectReference Include="..\Azaion.Common\Azaion.Common.csproj" />
<ProjectReference Include="..\Azaion.Dataset\Azaion.Dataset.csproj" />
</ItemGroup>
</Project>
+29
View File
@@ -0,0 +1,29 @@
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using Azaion.Suite.Services.DTO;
using Microsoft.Extensions.Options;
namespace Azaion.Suite;
public class DynamicAssemblyLoader(IOptions<LocalFilesConfig> localFilesConfig) : AssemblyLoadContext
{
private static readonly Dictionary<string?, Assembly> LoadedAssemblies = new();
static DynamicAssemblyLoader()
{
LoadedAssemblies = Default.Assemblies.ToDictionary(a => a.GetName().Name, a => a);
}
protected override Assembly Load(AssemblyName assemblyName)
{
var assembly = LoadedAssemblies.GetValueOrDefault(assemblyName.Name);
if (assembly != null)
return assembly;
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
var asm = Assembly.LoadFile(Path.Combine(currentLocation, localFilesConfig.Value.DllPath, $"{assemblyName.Name!}.dll"));
return asm;
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Azaion.Suite;
public class HardwareInfo
{
public string CPU { get; set; } = null!;
public string GPU { get; set; } = null!;
public string Memory { get; set; } = null!;
public string Hash { get; set; } = null!;
}
+126
View File
@@ -0,0 +1,126 @@
<Window x:Class="Azaion.Suite.Loader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
WindowStartupLocation="CenterScreen"
Title="Azaion Annotator Security"
Height="280" Width="350">
<Border Width="350"
Height="280"
Background="DarkGray"
CornerRadius="15"
MouseMove="MainMouseMove">
<Border.Effect>
<DropShadowEffect BlurRadius="15"
Direction ="-90"
RenderingBias ="Quality"
ShadowDepth ="2"
Color ="Gray" />
</Border.Effect>
<StackPanel Orientation="Vertical"
Margin="20">
<Canvas>
<Button Padding="5" ToolTip="Закрити" Background="DarkGray" BorderBrush="DarkGray" Canvas.Left="290" Cursor="Hand"
Name="CloseBtn"
Click="CloseClick">
<Path Stretch="Fill" Fill="LightGray" Data="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166
4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166
19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289
17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" />
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Canvas>
<TextBlock Text="Вхід"
FontSize="25"
HorizontalAlignment="Center"
VerticalAlignment="Top"
FontWeight="Bold"
/>
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Email"
Grid.Row="0"
Margin="0, 20, 0, 5"
HorizontalAlignment="Left"/>
<TextBox
Name="TbEmail"
Grid.Row="1"
Padding="0,5"
Width="300"
FontSize="16"
Background="DarkGray"
BorderBrush="DimGray"
BorderThickness="0,0,0,1"
HorizontalAlignment="Left"
Text="admin@azaion.com"/>
<TextBlock Text="Пароль"
Grid.Row="2"
Margin="0, 20, 0, 5"
HorizontalAlignment="Left"/>
<PasswordBox Grid.Row="3"
Name="TbPassword"
Password="Az@1on1000Odm$n"
FontSize="16"
Background="DarkGray"
BorderBrush="DimGray"
Padding="0,5"
Width="300"
BorderThickness="0,0,0,1"
HorizontalAlignment="Left"/>
</Grid>
<Button x:Name="LoginBtn"
Content="Вхід"
Foreground="White"
Background="DimGray"
Margin="0,25"
Height="35"
Width="280"
Cursor="Hand"
Click="RunClick">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="LoginBorder" Background="{TemplateBinding Background}"
CornerRadius="16">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray" TargetName="LoginBorder" />
<Setter Property="TextBlock.Foreground" Value="Black" TargetName="LoginBorder" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Border>
</Window>
+58
View File
@@ -0,0 +1,58 @@
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Azaion.Suite.Services.DTO;
using Microsoft.Extensions.Options;
namespace Azaion.Suite;
public partial class Loader : Window
{
private readonly IResourceLoader _resourceLoader;
private readonly IOptions<LocalFilesConfig> _localFilesConfig;
public Loader(IResourceLoader resourceLoader, IOptions<LocalFilesConfig> localFilesConfig)
{
_resourceLoader = resourceLoader;
_localFilesConfig = localFilesConfig;
InitializeComponent();
}
private async void RunClick(object sender, RoutedEventArgs e)
{
var stream = new MemoryStream();
await _resourceLoader.LoadAnnotator(TbEmail.Text, TbPassword.Password, stream);
stream.Seek(0, SeekOrigin.Begin);
var loader = new AssemblyLoadContext("DynamicContext", isCollectible: true);
var annotatorAssembly = loader.LoadFromStream(stream);
var appType = annotatorAssembly.GetType("Azaion.Annotator.App");
var appInstance = Activator.CreateInstance(appType);
var runMethod = appType.GetMethod("Run", BindingFlags.Public | BindingFlags.Instance);
if (runMethod != null)
{
runMethod.Invoke(appInstance, null);
}
// var entryPoint = annotatorAssembly.EntryPoint;
// if (entryPoint == null)
// return;
//
// var o = annotatorAssembly.CreateInstance(entryPoint.Name);
// entryPoint.Invoke(o, null);
}
private void CloseClick(object sender, RoutedEventArgs e) => Close();
private void MainMouseMove(object sender, MouseEventArgs e)
{
if (e.OriginalSource is Button || e.OriginalSource is TextBox)
return;
if (e.LeftButton == MouseButtonState.Pressed)
DragMove();
}
}
+12
View File
@@ -0,0 +1,12 @@
<Window x:Class="Azaion.Suite.MainSuite"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Azaion.Suite"
mc:Ignorable="d"
Title="MainSuite" Height="450" Width="800">
<Grid>
</Grid>
</Window>
+59
View File
@@ -0,0 +1,59 @@
using System.IO;
using System.Windows;
using Azaion.Annotator.Extensions;
using Azaion.Common.DTO.Config;
using Microsoft.Extensions.Options;
namespace Azaion.Suite;
public partial class MainSuite : Window
{
private readonly AppConfig _appConfig;
private readonly IConfigUpdater _configUpdater;
public MainSuite(IOptions<AppConfig> appConfig, IConfigUpdater configUpdater)
{
_configUpdater = configUpdater;
_appConfig = appConfig.Value;
InitializeComponent();
Loaded += OnLoaded;
Closed += OnFormClosed;
SizeChanged += async (_, _) => await SaveUserSettings();
LocationChanged += async (_, _) => await SaveUserSettings();
StateChanged += async (_, _) => await SaveUserSettings();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (!Directory.Exists(_appConfig.DirectoriesConfig.LabelsDirectory))
Directory.CreateDirectory(_appConfig.DirectoriesConfig.LabelsDirectory);
if (!Directory.Exists(_appConfig.DirectoriesConfig.ImagesDirectory))
Directory.CreateDirectory(_appConfig.DirectoriesConfig.ImagesDirectory);
if (!Directory.Exists(_appConfig.DirectoriesConfig.ResultsDirectory))
Directory.CreateDirectory(_appConfig.DirectoriesConfig.ResultsDirectory);
Left = _appConfig.WindowConfig.WindowLocation.X;
Top = _appConfig.WindowConfig.WindowLocation.Y;
Width = _appConfig.WindowConfig.WindowSize.Width;
Height = _appConfig.WindowConfig.WindowSize.Height;
if (_appConfig.WindowConfig.FullScreen)
WindowState = WindowState.Maximized;
}
private async Task SaveUserSettings()
{
await ThrottleExt.Throttle(() =>
{
_configUpdater.Save(_appConfig);
return Task.CompletedTask;
}, TimeSpan.FromSeconds(5));
}
private void OnFormClosed(object? sender, EventArgs e)
{
_configUpdater.Save(_appConfig);
}
}
+38
View File
@@ -0,0 +1,38 @@
using System.IO;
using System.Reflection;
using Azaion.Suite.Services;
using Azaion.Suite.Services.DTO;
using Microsoft.Extensions.Options;
namespace Azaion.Suite;
public interface IResourceLoader
{
Task LoadAnnotator(string email, string password, Stream outStream, CancellationToken cancellationToken = default);
Assembly LoadAssembly(string name, CancellationToken cancellationToken = default);
}
public class ResourceLoader(AzaionApiClient azaionApi, IHardwareService hardwareService, IOptions<LocalFilesConfig> localFilesConfig) : IResourceLoader
{
public async Task LoadAnnotator(string email, string password, Stream outStream, CancellationToken cancellationToken = default)
{
var hardwareInfo = await hardwareService.GetHardware();
azaionApi.Login(email, password);
var key = Security.MakeEncryptionKey(email, password, hardwareInfo.Hash);
var encryptedStream = await azaionApi.GetResource(password, hardwareInfo, ResourceEnum.AnnotatorDll);
await encryptedStream.DecryptTo(outStream, key, cancellationToken);
//return Assembly.Load(stream.ToArray());
}
public Assembly LoadAssembly(string name, CancellationToken cancellationToken = default)
{
var dllValues = name.Split(",");
var dllName = $"{dllValues[0]}.dll";
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
var asm = Assembly.LoadFile(Path.Combine(currentLocation, localFilesConfig.Value.DllPath, dllName));
return asm;
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Text;
using Azaion.Suite.Services.DTO;
using Newtonsoft.Json;
namespace Azaion.Suite.Services;
public class AzaionApiClient(HttpClient httpClient)
{
const string JSON_MEDIA = "application/json";
private string Email { get; set; } = null!;
private SecureString Password { get; set; } = new();
private string JwtToken { get; set; } = null!;
public void Login(string email, string password)
{
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
throw new Exception("Email or password is empty!");
Email = email;
Password = password.ToSecureString();
}
public async Task<Stream> GetResource(string password, HardwareInfo hardware, ResourceEnum resourceEnum)
{
var response = await Send(httpClient, new HttpRequestMessage(HttpMethod.Post, "/resources/get")
{
Content = new StringContent(JsonConvert.SerializeObject(new { password, hardware, resourceEnum }), Encoding.UTF8, JSON_MEDIA)
});
return await response.Content.ReadAsStreamAsync();
}
private async Task<string> Authorize()
{
if (string.IsNullOrEmpty(Email) || Password.Length == 0)
throw new Exception("Email or password is empty! Please do Login first!");
var payload = new
{
email = Email,
password = Password.ToRealString()
};
var response = await httpClient.PostAsync(
"login",
new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, JSON_MEDIA));
if (!response.IsSuccessStatusCode)
throw new Exception($"Login failed: {response.StatusCode}");
var responseData = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<LoginResponse>(responseData);
if (string.IsNullOrEmpty(result?.Token))
throw new Exception("JWT Token not found in response");
return result.Token;
}
private async Task<HttpResponseMessage> Send(HttpClient client, HttpRequestMessage request)
{
if (string.IsNullOrEmpty(JwtToken))
JwtToken = await Authorize();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", JwtToken);
var response = await client.SendAsync(request);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
JwtToken = await Authorize();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", JwtToken);
response = await client.SendAsync(request);
}
if (response.IsSuccessStatusCode)
return response;
var result = await response.Content.ReadAsStringAsync();
throw new Exception($"Failed: {response.StatusCode}! Result: {result}");
}
}
@@ -0,0 +1,9 @@
namespace Azaion.Suite.Services.DTO;
public enum ResourceEnum
{
None = 0,
AnnotatorDll = 10,
AIModelRKNN = 20,
AIModelONNX = 30,
}
+107
View File
@@ -0,0 +1,107 @@
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
namespace Azaion.Suite.Services;
public interface IHardwareService
{
Task<HardwareInfo> GetHardware();
}
public class HardwareService : IHardwareService
{
private const string WIN32_GET_HARDWARE_COMMAND =
"wmic OS get TotalVisibleMemorySize /Value && " +
"wmic CPU get Name /Value && " +
"wmic path Win32_VideoController get Name /Value";
private const string UNIX_GET_HARDWARE_COMMAND =
"/bin/bash -c \"free -g | grep Mem: | awk '{print $2}' && " +
"lscpu | grep 'Model name:' | cut -d':' -f2 && " +
"lspci | grep VGA | cut -d':' -f3\"";
public async Task<HardwareInfo> GetHardware()
{
try
{
var output = await RunCommand(Environment.OSVersion.Platform == PlatformID.Win32NT
? WIN32_GET_HARDWARE_COMMAND
: UNIX_GET_HARDWARE_COMMAND);
var lines = output
.Replace("TotalVisibleMemorySize=", "")
.Replace("Name=", "")
.Replace(" ", " ")
.Trim()
.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
var memoryStr = "Unknown RAM";
if (lines.Length > 0)
{
memoryStr = lines[0];
if (int.TryParse(memoryStr, out var memKb))
memoryStr = $"{Math.Round(memKb / 1024.0 / 1024.0)} Gb";
}
var hardwareInfo = new HardwareInfo
{
Memory = memoryStr,
CPU = lines.Length > 1 && string.IsNullOrEmpty(lines[1])
? "Unknown RAM"
: lines[1],
GPU = lines.Length > 2 && string.IsNullOrEmpty(lines[2])
? "Unknown GPU"
: lines[2]
};
hardwareInfo.Hash = ToHash($"Azaion_{MacAddress()}_{hardwareInfo.CPU}_{hardwareInfo.GPU}");
return hardwareInfo;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
private string MacAddress()
{
var macAddress = NetworkInterface
.GetAllNetworkInterfaces()
.Where(nic => nic.OperationalStatus == OperationalStatus.Up)
.Select(nic => nic.GetPhysicalAddress().ToString())
.FirstOrDefault();
return macAddress ?? string.Empty;
}
private async Task<string> RunCommand(string command)
{
try
{
using var process = new Process();
process.StartInfo.FileName = Environment.OSVersion.Platform == PlatformID.Unix ? "/bin/bash" : "cmd.exe";
process.StartInfo.Arguments = Environment.OSVersion.Platform == PlatformID.Unix
? $"-c \"{command}\""
: $"/c {command}";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.Start();
var result = await process.StandardOutput.ReadToEndAsync();
await process.WaitForExitAsync();
return result.Trim();
}
catch
{
return string.Empty;
}
}
private static string ToHash(string str) =>
Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str)));
}
+83
View File
@@ -0,0 +1,83 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Text;
namespace Azaion.Suite.Services;
public static class Security
{
private const int BUFFER_SIZE = 524288; // 512 KB buffer size
public static string ToHash(this string str) =>
Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str)));
public static string MakeEncryptionKey(string email, string password, string? hardwareHash) =>
$"{email}-{password}-{hardwareHash}-#%@AzaionKey@%#---".ToHash();
public static SecureString ToSecureString(this string str)
{
var secureString = new SecureString();
foreach (var c in str.ToCharArray())
secureString.AppendChar(c);
return secureString;
}
public static string? ToRealString(this SecureString value)
{
var valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
return Marshal.PtrToStringUni(valuePtr);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
}
public static async Task EncryptTo(this Stream stream, Stream toStream, string key, CancellationToken cancellationToken = default)
{
if (stream is { CanRead: false }) throw new ArgumentNullException(nameof(stream));
if (key is not { Length: > 0 }) throw new ArgumentNullException(nameof(key));
using var aes = Aes.Create();
aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key));
aes.GenerateIV();
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
await using var cs = new CryptoStream(toStream, encryptor, CryptoStreamMode.Write, leaveOpen: true);
// Prepend IV to the encrypted data
await toStream.WriteAsync(aes.IV.AsMemory(0, aes.IV.Length), cancellationToken);
var buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, cancellationToken)) > 0)
await cs.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
}
public static async Task DecryptTo(this Stream encryptedStream, Stream toStream, string key, CancellationToken cancellationToken = default)
{
using var aes = Aes.Create();
aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key));
// Read the IV from the start of the input stream
var iv = new byte[aes.BlockSize / 8];
_ = await encryptedStream.ReadAsync(iv, cancellationToken);
aes.IV = iv;
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
await using var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read, leaveOpen: true);
// Read and write in chunks
var buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = await cryptoStream.ReadAsync(buffer, cancellationToken)) > 0)
await toStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"ApiConfig": {
"Url": "https://api.azaion.com",
"TimeoutSeconds": 20,
"RetryCount": 3
},
"LocalFilesConfig": {
"DllPath": "AzaionSuite"
}
}