Compare commits
21 Commits
878d26653c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f9e8e084a | |||
| 01a54f32af | |||
| 8b8447649a | |||
| c11911c4a7 | |||
| eb5849c16d | |||
| 08a954f836 | |||
| c37e8d3fb4 | |||
| 1d6c8de71b | |||
| adbee34498 | |||
| 0f88e82e28 | |||
| 3658bdcba0 | |||
| 4d51d4f00e | |||
| 1ec090eca4 | |||
| 35c1665950 | |||
| bd191b113a | |||
| c50477bbd3 | |||
| a3349e23c9 | |||
| 8027229a7f | |||
| 5893d86e13 | |||
| ee9bc9ad33 | |||
| 76c9bd06af |
18
Akari.Prototype.Client/Akari.Prototype.Client.csproj
Normal file
18
Akari.Prototype.Client/Akari.Prototype.Client.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.6" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.6" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.6" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.37.0" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="1.3.1" />
|
||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.2.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Akari.Prototype.Shared\Akari.Prototype.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
7
Akari.Prototype.Client/App.axaml
Normal file
7
Akari.Prototype.Client/App.axaml
Normal file
@@ -0,0 +1,7 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="Akari.Prototype.Client.App">
|
||||
<Application.Styles>
|
||||
<FluentTheme Mode="Light"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
24
Akari.Prototype.Client/App.axaml.cs
Normal file
24
Akari.Prototype.Client/App.axaml.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Akari.Prototype.Client
|
||||
{
|
||||
public class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow();
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Akari.Prototype.Client/MainWindow.axaml
Normal file
80
Akari.Prototype.Client/MainWindow.axaml
Normal file
@@ -0,0 +1,80 @@
|
||||
<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="Akari.Prototype.Client.MainWindow"
|
||||
Title="Akari.Prototype.Client"
|
||||
Width="600"
|
||||
Height="350"
|
||||
ExtendClientAreaToDecorationsHint="True"
|
||||
TransparencyLevelHint="AcrylicBlur"
|
||||
ExtendClientAreaTitleBarHeightHint="-1"
|
||||
Background="{x:Null}">
|
||||
<Panel>
|
||||
<ExperimentalAcrylicBorder IsHitTestVisible="False">
|
||||
<ExperimentalAcrylicBorder.Material>
|
||||
<ExperimentalAcrylicMaterial BackgroundSource="Digger" TintColor="#F8F8F0" TintOpacity="1" MaterialOpacity="0.6" />
|
||||
</ExperimentalAcrylicBorder.Material>
|
||||
</ExperimentalAcrylicBorder>
|
||||
<RelativePanel VerticalAlignment="Center" Margin="20">
|
||||
<TextBlock x:Name="TxtName"
|
||||
Margin="5, 15"
|
||||
Text="Application Name:" />
|
||||
<TextBox x:Name="BoxName"
|
||||
RelativePanel.RightOf="TxtName"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWith="TxtName" />
|
||||
|
||||
<TextBlock x:Name="TxtToken"
|
||||
RelativePanel.Below="TxtName"
|
||||
RelativePanel.AlignRightWith="TxtName"
|
||||
Margin="5, 15"
|
||||
Text="Token:" />
|
||||
<TextBox x:Name="BoxToken"
|
||||
RelativePanel.RightOf="TxtToken"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignLeftWith="BoxName"
|
||||
RelativePanel.AlignVerticalCenterWith="TxtToken"
|
||||
PasswordChar="*" />
|
||||
|
||||
<TextBlock x:Name="TxtMode"
|
||||
RelativePanel.Below="TxtToken"
|
||||
RelativePanel.AlignRightWith="TxtToken"
|
||||
Margin="5, 15"
|
||||
Text="Mode:" />
|
||||
<ComboBox x:Name="CbxMode"
|
||||
RelativePanel.RightOf="TxtMode"
|
||||
RelativePanel.AlignVerticalCenterWith="TxtMode"
|
||||
MinWidth="100"
|
||||
SelectedIndex="0">
|
||||
<ComboBoxItem>Encrypt</ComboBoxItem>
|
||||
<ComboBoxItem>Decrypt</ComboBoxItem>
|
||||
</ComboBox>
|
||||
|
||||
<Button x:Name="BtnPickFile"
|
||||
RelativePanel.RightOf="CbxMode"
|
||||
RelativePanel.AlignVerticalCenterWith="TxtMode"
|
||||
Margin="15, 0, 5, 0"
|
||||
Content="Pick File"
|
||||
Click="OnPickFile" />
|
||||
<TextBlock x:Name="TxtFile"
|
||||
RelativePanel.RightOf="BtnPickFile"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWith="BtnPickFile"
|
||||
MaxWidth="250"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="N/A" />
|
||||
|
||||
<Button x:Name="BtnSubmit"
|
||||
RelativePanel.Below="TxtMode"
|
||||
RelativePanel.AlignLeftWith="BoxToken"
|
||||
RelativePanel.AlignRightWith="BoxToken"
|
||||
HorizontalContentAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0, 5"
|
||||
Content="Submit"
|
||||
Click="OnSubmit" />
|
||||
</RelativePanel>
|
||||
</Panel>
|
||||
</Window>
|
||||
134
Akari.Prototype.Client/MainWindow.axaml.cs
Normal file
134
Akari.Prototype.Client/MainWindow.axaml.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Akari.Prototype.Shared.Protos;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Net.Client;
|
||||
using MessageBox.Avalonia;
|
||||
|
||||
namespace Akari.Prototype.Client
|
||||
{
|
||||
public partial class MainWindow : Window, IDisposable
|
||||
{
|
||||
public const string Hostname = "https://localhost:5001";
|
||||
|
||||
private readonly GrpcChannel _channel;
|
||||
|
||||
private string? _file = null;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_channel = GrpcChannel.ForAddress(Hostname);
|
||||
}
|
||||
|
||||
private async void OnPickFile(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var mode = GetMode();
|
||||
|
||||
var dialog = new OpenFileDialog()
|
||||
{
|
||||
AllowMultiple = false,
|
||||
Title = $"Pick a file to {mode}"
|
||||
};
|
||||
|
||||
if (await dialog.ShowAsync(this) is string[] { Length: 1 } res)
|
||||
{
|
||||
_file = res[0];
|
||||
TxtFile.Text = Path.GetFileName(_file);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnSubmit(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_file is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BoxName.Text))
|
||||
{
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"Invalid application name", icon: MessageBox.Avalonia.Enums.Icon.Error).Show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BoxToken.Text))
|
||||
{
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"Invalid token", icon: MessageBox.Avalonia.Enums.Icon.Error).Show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var client = new AkariApi.AkariApiClient(_channel);
|
||||
|
||||
// Decrypt
|
||||
if (GetMode() == Mode.Decrypt)
|
||||
{
|
||||
var response = await client.DecryptAsync(new DecryptRequest()
|
||||
{
|
||||
Application = BoxName.Text,
|
||||
Token = BoxToken.Text,
|
||||
Encrypted = ByteString.CopyFrom(File.ReadAllBytes(_file))
|
||||
});
|
||||
|
||||
if (response.ResponseCase == DecryptResponse.ResponseOneofCase.ErrorMessage)
|
||||
{
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"Error during decryption, server response: {response.ErrorMessage}", icon: MessageBox.Avalonia.Enums.Icon.Error).Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
string outPath = Path.ChangeExtension(_file, null);
|
||||
|
||||
File.WriteAllBytes(outPath, response.Plain.ToByteArray());
|
||||
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Success", $"Saved decrypted file to {outPath}\n").Show();
|
||||
|
||||
}
|
||||
}
|
||||
// Encrypt
|
||||
else
|
||||
{
|
||||
var response = await client.EncryptAsync(new EncryptRequest()
|
||||
{
|
||||
Application = BoxName.Text,
|
||||
Token = BoxToken.Text,
|
||||
Plain = ByteString.CopyFrom(File.ReadAllBytes(_file))
|
||||
});
|
||||
|
||||
if (response.ResponseCase == EncryptResponse.ResponseOneofCase.ErrorMessage)
|
||||
{
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"Error during encryption, server response: {response.ErrorMessage}", icon: MessageBox.Avalonia.Enums.Icon.Error).Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
string outPath = $"{_file}.enc";
|
||||
|
||||
File.WriteAllBytes(outPath, response.Encrypted.ToByteArray());
|
||||
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Success", $"Saved encrypted file to {outPath}").Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Mode GetMode()
|
||||
{
|
||||
if (CbxMode.SelectedItem is ComboBoxItem { Content: string mode })
|
||||
{
|
||||
return Enum.Parse<Mode>(mode);
|
||||
}
|
||||
|
||||
throw new Exception("Invalid mode in CbxMode");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_channel.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Akari.Prototype.Client/Mode.cs
Normal file
14
Akari.Prototype.Client/Mode.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Client
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Encrypt,
|
||||
Decrypt
|
||||
}
|
||||
}
|
||||
22
Akari.Prototype.Client/Program.cs
Normal file
22
Akari.Prototype.Client/Program.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
|
||||
namespace Akari.Prototype.Client
|
||||
{
|
||||
class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace();
|
||||
}
|
||||
}
|
||||
11
Akari.Prototype.Client/nuget.config
Normal file
11
Akari.Prototype.Client/nuget.config
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
To use the Avalonia CI feed to get unstable packages, move this file to the root of your solution.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="AvaloniaCI" value="https://www.myget.org/F/avalonia-ci/api/v2" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
@@ -1,13 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CliFx" Version="2.0.4" />
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.34.0" />
|
||||
@@ -15,4 +11,8 @@
|
||||
<PackageReference Include="Spectre.Console" Version="0.39.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Akari.Prototype.Shared\Akari.Prototype.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Akari.Prototype.Server.Options;
|
||||
using Akari.Prototype.Server.Services;
|
||||
using Akari.Prototype.Server.Utils;
|
||||
using Akari.Prototype.Server.Utils.Extensions;
|
||||
@@ -9,6 +11,10 @@ using CliFx;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace Akari.Prototype.Server.Cli.Commands
|
||||
@@ -61,7 +67,7 @@ namespace Akari.Prototype.Server.Cli.Commands
|
||||
}
|
||||
}
|
||||
|
||||
[Command("fp r", Description = "Remove an application")]
|
||||
[Command("app r", Description = "Remove an application")]
|
||||
public class RemoveApplicationCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0, Description = "The application name")]
|
||||
@@ -81,12 +87,77 @@ namespace Akari.Prototype.Server.Cli.Commands
|
||||
throw new CommandException($"The application {Name} doesn't exist");
|
||||
}
|
||||
|
||||
console.AsAnsiConsole().Markup($"[green]Successfully deleted {Name}[/]");
|
||||
console.AsAnsiConsole().MarkupLine($"[green]Successfully deleted {Name}[/]");
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
[Command("app f", Description = "Add a fingerprint to and application")]
|
||||
public class AddFingerprintToApplicationCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0, Description = "Application to add fingerprint to")]
|
||||
public string Application { get; init; }
|
||||
|
||||
[CommandParameter(1, Description = "Fingerprint to add")]
|
||||
public string Fingerprint { get; init; }
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IApplicationsManager _applications;
|
||||
private readonly IMasterKeyService _masterKeyService;
|
||||
|
||||
public AddFingerprintToApplicationCommand(IServiceProvider serviceProvider, IApplicationsManager applications, IMasterKeyService masterKeyService)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_applications = applications;
|
||||
_masterKeyService = masterKeyService;
|
||||
}
|
||||
|
||||
public async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var ansiConsole = console.AsAnsiConsole();
|
||||
|
||||
if (!_applications.Contains(Application))
|
||||
{
|
||||
throw new CommandException("No application exist with this name");
|
||||
}
|
||||
|
||||
if (_applications.IsFingerprintRegistered(Application, Fingerprint))
|
||||
{
|
||||
throw new CommandException($"'{Fingerprint}' is already registered for '{Application}'");
|
||||
}
|
||||
|
||||
if (!CliUtils.Login(_masterKeyService, ansiConsole))
|
||||
{
|
||||
throw new CommandException("Can't proceed without master service login");
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
|
||||
var services = scope.ServiceProvider;
|
||||
|
||||
using var provider = new TcpProviderService(services.GetRequiredService<ILogger<TcpProviderService>>(),
|
||||
services.GetRequiredService<IOptions<TcpProviderOptions>>(),
|
||||
services.GetRequiredService<IHostApplicationLifetime>(),
|
||||
services.GetRequiredService<IFingerprintManager>());
|
||||
|
||||
var source = new CancellationTokenSource();
|
||||
|
||||
await provider.StartAsync(source.Token);
|
||||
|
||||
ansiConsole.WriteLine("Press a key when the fingerprint has been auth");
|
||||
|
||||
console.Input.Read();
|
||||
|
||||
if (!_applications.AddFingerprint(Application, Fingerprint))
|
||||
{
|
||||
throw new CommandException($"Fingerprint '{Fingerprint}' has not been auth");
|
||||
}
|
||||
|
||||
ansiConsole.WriteLine($"[green]Successfully added '{Fingerprint}' to '{Application}'[/]");
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IApplicationsManager _applications;
|
||||
|
||||
public ApplicationCommands(IApplicationsManager applications)
|
||||
@@ -101,12 +172,12 @@ namespace Akari.Prototype.Server.Cli.Commands
|
||||
var table = new Table();
|
||||
|
||||
table.AddColumn(new TableColumn("[bold yellow]Name[/]").LeftAligned());
|
||||
table.AddColumn(new TableColumn("[bold blue]Token Hash[/]").LeftAligned());
|
||||
table.AddColumn(new TableColumn("[bold blue]Fingerprints[/]").LeftAligned());
|
||||
table.AddColumn(new TableColumn("[bold blue]Token Hash[/]").LeftAligned());
|
||||
|
||||
foreach (var application in _applications)
|
||||
{
|
||||
table.AddRow($"[silver]{application.Name}[/]", $"[grey]{application.TokenHash}[/]", $"[grey]{application.Fingerprints}[/]");
|
||||
table.AddRow($"[silver]{application.Name}[/]", $"[grey]{string.Join(", ", application.Fingerprints)}[/]", $"[grey]{application.TokenHash}[/]");
|
||||
}
|
||||
|
||||
ansiConsole.Write(table);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Akari.Prototype.Server.Services;
|
||||
using Akari.Prototype.Server.Options;
|
||||
using Akari.Prototype.Server.Services;
|
||||
using Akari.Prototype.Server.Utils;
|
||||
using Akari.Prototype.Server.Utils.Extensions;
|
||||
using CliFx;
|
||||
@@ -6,12 +7,17 @@ using CliFx.Attributes;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Extensibility;
|
||||
using CliFx.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Spectre.Console;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Server.Cli.Commands
|
||||
@@ -46,6 +52,11 @@ namespace Akari.Prototype.Server.Cli.Commands
|
||||
throw new CommandException("Name can't be more than 200 bytes (UTF-8)");
|
||||
}
|
||||
|
||||
if (Name.Contains('$'))
|
||||
{
|
||||
throw new CommandException("Name can't contain a '$'");
|
||||
}
|
||||
|
||||
if (_fingerprintManager.Contains(Name))
|
||||
{
|
||||
throw new CommandException("A fingerprint with this name is already registered.");
|
||||
@@ -88,12 +99,66 @@ namespace Akari.Prototype.Server.Cli.Commands
|
||||
throw new CommandException($"The fingerprint {Name} doesn't exist");
|
||||
}
|
||||
|
||||
console.AsAnsiConsole().Markup($"[green]Successfully deleted {Name}[/]");
|
||||
console.AsAnsiConsole().MarkupLine($"[green]Successfully deleted {Name}[/]");
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
[Command("fp t", Description = "Test a fingerprint")]
|
||||
public class TestFingerprintCommand : ICommand
|
||||
{
|
||||
public const int AuthTimeout = 5_000;
|
||||
|
||||
[CommandParameter(0, Description = "The fingerprint name")]
|
||||
public string Name { get; init; }
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IAuthManager _authManager;
|
||||
private readonly IFingerprintManager _fingerprintManager;
|
||||
|
||||
public TestFingerprintCommand(IServiceProvider serviceProvider, IAuthManager authManager, IFingerprintManager fingerprintManager)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_authManager = authManager;
|
||||
_fingerprintManager = fingerprintManager;
|
||||
}
|
||||
|
||||
public async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var ansiConsole = console.AsAnsiConsole();
|
||||
|
||||
if (!_fingerprintManager.Contains(Name))
|
||||
{
|
||||
throw new CommandException("This fingerprint does not exist");
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
|
||||
var services = scope.ServiceProvider;
|
||||
|
||||
using var provider = new TcpProviderService(services.GetRequiredService<ILogger<TcpProviderService>>(),
|
||||
services.GetRequiredService<IOptions<TcpProviderOptions>>(),
|
||||
services.GetRequiredService<IHostApplicationLifetime>(),
|
||||
services.GetRequiredService<IFingerprintManager>());
|
||||
|
||||
var source = new CancellationTokenSource(AuthTimeout);
|
||||
|
||||
await provider.StartAsync(source.Token);
|
||||
|
||||
ansiConsole.MarkupLine($"Waiting for [yellow]{Name}[/] auth");
|
||||
|
||||
await Task.Delay(AuthTimeout);
|
||||
|
||||
if (!_authManager.TryGetKey(Name, out _))
|
||||
{
|
||||
throw new CommandException("Timed out while waiting for fingerprint auth");
|
||||
}
|
||||
|
||||
ansiConsole.MarkupLine($"[green]Successfully retrieved '{Name}' key[/]");
|
||||
}
|
||||
}
|
||||
|
||||
public FingerprintCommands(IFingerprintManager fingerprintManager)
|
||||
{
|
||||
_fingerprintManager = fingerprintManager;
|
||||
|
||||
127
Akari.Prototype.Server/Cli/Commands/MasterCommands.cs
Normal file
127
Akari.Prototype.Server/Cli/Commands/MasterCommands.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Akari.Prototype.Server.Services;
|
||||
using Akari.Prototype.Server.Utils;
|
||||
using Akari.Prototype.Server.Utils.Extensions;
|
||||
using CliFx;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace Akari.Prototype.Server.Cli.Commands
|
||||
{
|
||||
[Command("m", Description = "Manage master password")]
|
||||
public class MasterCommands : ICommand
|
||||
{
|
||||
[Command("m c", Description = "Create master password")]
|
||||
public class CreateMasterPasswordCommand : ICommand
|
||||
{
|
||||
private readonly IMasterKeyService _masterKeyService;
|
||||
|
||||
public CreateMasterPasswordCommand(IMasterKeyService masterKeyService)
|
||||
{
|
||||
_masterKeyService = masterKeyService;
|
||||
}
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var ansiConsole = console.AsAnsiConsole();
|
||||
|
||||
if (_masterKeyService.IsPasswordSet)
|
||||
{
|
||||
throw new CommandException("Can't create master password if it's already set");
|
||||
}
|
||||
|
||||
ansiConsole.WriteLine();
|
||||
|
||||
string password = ansiConsole.Prompt(
|
||||
new TextPrompt<string>("Enter [yellow]master password[/]")
|
||||
.PromptStyle("red")
|
||||
.Secret()
|
||||
);
|
||||
|
||||
string passwordConfirm = ansiConsole.Prompt(
|
||||
new TextPrompt<string>("Confirm [yellow]master password[/]")
|
||||
.PromptStyle("red")
|
||||
.Secret()
|
||||
);
|
||||
|
||||
ansiConsole.WriteLine();
|
||||
|
||||
if (password != passwordConfirm)
|
||||
{
|
||||
throw new CommandException("Password doesn't match");
|
||||
}
|
||||
|
||||
if (!_masterKeyService.CreatePassword(password))
|
||||
{
|
||||
throw new CommandException("Created master password but can't login");
|
||||
}
|
||||
|
||||
ansiConsole.MarkupLine("[green]Successfully created [yellow]master password[/][/]");
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
[Command("m t", Description = "Test master password")]
|
||||
public class TestMasterPasswordCommand : ICommand
|
||||
{
|
||||
private readonly IMasterKeyService _masterKeyService;
|
||||
|
||||
public TestMasterPasswordCommand(IMasterKeyService masterKeyService)
|
||||
{
|
||||
_masterKeyService = masterKeyService;
|
||||
}
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var ansiConsole = console.AsAnsiConsole();
|
||||
|
||||
if (!_masterKeyService.IsPasswordSet)
|
||||
{
|
||||
throw new CommandException("Can't login if no master password has been set");
|
||||
}
|
||||
|
||||
ansiConsole.WriteLine();
|
||||
|
||||
if (!CliUtils.Login(_masterKeyService, ansiConsole))
|
||||
{
|
||||
ansiConsole.MarkupLine("[red]Invalid [yellow]master password[/][/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
ansiConsole.MarkupLine("[green]Successfully logged in[/]");
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IMasterKeyService _masterKeyService;
|
||||
|
||||
public MasterCommands(IMasterKeyService masterKeyService)
|
||||
{
|
||||
_masterKeyService = masterKeyService;
|
||||
}
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var ansiConsole = console.AsAnsiConsole();
|
||||
|
||||
if (!_masterKeyService.IsPasswordSet)
|
||||
{
|
||||
ansiConsole.MarkupLine("[yellow]Master password[/] is not set");
|
||||
}
|
||||
else
|
||||
{
|
||||
ansiConsole.MarkupLine("[yellow]Master password[/] is set");
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
{
|
||||
"profiles": {
|
||||
"Akari.Prototype.Server": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": "true",
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5000;https://localhost:5001",
|
||||
"workingDirectory": ".\\bin\\Debug\\net5.0",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"dotnetRunMessages": "true",
|
||||
"applicationUrl": "http://localhost:5000;https://localhost:5001"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "Akari.Prototype.Server";
|
||||
|
||||
package greet;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply);
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings.
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
109
Akari.Prototype.Server/Services/AkariService.cs
Normal file
109
Akari.Prototype.Server/Services/AkariService.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Akari.Prototype.Server.Services;
|
||||
using Akari.Prototype.Server.Utils;
|
||||
using Akari.Prototype.Shared.Protos;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Server
|
||||
{
|
||||
public class AkariService : AkariApi.AkariApiBase
|
||||
{
|
||||
private readonly ILogger<AkariService> _logger;
|
||||
private readonly IApplicationsManager _applications;
|
||||
|
||||
public AkariService(ILogger<AkariService> logger, IApplicationsManager applications)
|
||||
{
|
||||
_logger = logger;
|
||||
_applications = applications;
|
||||
}
|
||||
|
||||
public override Task<DecryptResponse> Decrypt(DecryptRequest request, ServerCallContext context)
|
||||
{
|
||||
_logger.LogDebug($"Received decrypt request: {request}");
|
||||
|
||||
if (!_applications.Contains(request.Application))
|
||||
{
|
||||
_logger.LogDebug($"Application not found: {request.Application}");
|
||||
|
||||
return Task.FromResult(new DecryptResponse()
|
||||
{
|
||||
ErrorMessage = "Application not found"
|
||||
});
|
||||
}
|
||||
|
||||
if (!_applications.VerifyToken(request.Application, request.Token))
|
||||
{
|
||||
_logger.LogDebug($"Invalid token: {request.Token}");
|
||||
|
||||
return Task.FromResult(new DecryptResponse()
|
||||
{
|
||||
ErrorMessage = "Invalid token"
|
||||
});
|
||||
}
|
||||
|
||||
if (_applications.TryRetrieveKey(request.Application, request.Token, out var key))
|
||||
{
|
||||
_logger.LogDebug($"Key retrieved, sending decrypted data...");
|
||||
|
||||
return Task.FromResult(new DecryptResponse()
|
||||
{
|
||||
Plain = ByteString.CopyFrom(Security.AesGcmDecrypt(key, request.Encrypted.ToByteArray()))
|
||||
});
|
||||
}
|
||||
|
||||
_logger.LogDebug($"No fingerprint auth found for {request.Application}");
|
||||
|
||||
return Task.FromResult(new DecryptResponse()
|
||||
{
|
||||
ErrorMessage = "No fingerprint auth found for this application"
|
||||
});
|
||||
}
|
||||
|
||||
public override Task<EncryptResponse> Encrypt(EncryptRequest request, ServerCallContext context)
|
||||
{
|
||||
_logger.LogDebug($"Received encrypt request: {request}");
|
||||
|
||||
if (!_applications.Contains(request.Application))
|
||||
{
|
||||
_logger.LogDebug($"Application not found: {request.Application}");
|
||||
|
||||
return Task.FromResult(new EncryptResponse()
|
||||
{
|
||||
ErrorMessage = "Application not found"
|
||||
});
|
||||
}
|
||||
|
||||
if (!_applications.VerifyToken(request.Application, request.Token))
|
||||
{
|
||||
_logger.LogDebug($"Invalid token: {request.Token}");
|
||||
|
||||
return Task.FromResult(new EncryptResponse()
|
||||
{
|
||||
ErrorMessage = "Wrong token"
|
||||
});
|
||||
}
|
||||
|
||||
if (_applications.TryRetrieveKey(request.Application, request.Token, out var key))
|
||||
{
|
||||
_logger.LogDebug($"Key retrieved, sending decrypted data...");
|
||||
|
||||
return Task.FromResult(new EncryptResponse()
|
||||
{
|
||||
Encrypted = ByteString.CopyFrom(Security.AesGcmEncrypt(key, request.Plain.ToByteArray()))
|
||||
});
|
||||
}
|
||||
|
||||
_logger.LogDebug($"No fingerprint auth found for {request.Application}");
|
||||
|
||||
return Task.FromResult(new EncryptResponse()
|
||||
{
|
||||
ErrorMessage = "No fingerprint auth found for this application"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,9 +108,12 @@ namespace Akari.Prototype.Server.Services
|
||||
return _applications.ContainsKey(applicationName);
|
||||
}
|
||||
|
||||
public bool IsFingerprintRegistered(string applicationName, string fingerprintName) => _applications.TryGetValue(applicationName, out var application)
|
||||
&& application.Fingerprints.Contains(fingerprintName);
|
||||
|
||||
public bool Remove(string applicationName)
|
||||
{
|
||||
if (!_applications.ContainsKey(applicationName))
|
||||
if (!_applications.TryGetValue(applicationName, out var application))
|
||||
{
|
||||
_logger.LogDebug($"Can't remove non existing application: {applicationName}");
|
||||
|
||||
@@ -118,9 +121,9 @@ namespace Akari.Prototype.Server.Services
|
||||
}
|
||||
|
||||
// Clear keys
|
||||
_keyManager.Clear(applicationName);
|
||||
_keyManager.Clear(application);
|
||||
|
||||
_applications.Remove(applicationName);
|
||||
_applications.Remove(application.Name);
|
||||
|
||||
SaveApplications();
|
||||
|
||||
@@ -129,6 +132,13 @@ namespace Akari.Prototype.Server.Services
|
||||
|
||||
public bool AddFingerprint(string applicationName, string fingerprintName)
|
||||
{
|
||||
if (IsFingerprintRegistered(applicationName, fingerprintName))
|
||||
{
|
||||
_logger.LogDebug($"'{fingerprintName}' is already registered for '{applicationName}'");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify master password
|
||||
if (!_masterKeyService.IsLoggedIn)
|
||||
{
|
||||
@@ -153,7 +163,7 @@ namespace Akari.Prototype.Server.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool VerifyToken(string applicationName, string applicationToken)
|
||||
public bool VerifyToken(string applicationName, string applicationToken)
|
||||
{
|
||||
if (!_applications.TryGetValue(applicationName, out var application))
|
||||
{
|
||||
@@ -184,6 +194,8 @@ namespace Akari.Prototype.Server.Services
|
||||
if (!_authManager.TryGetKey(application.Fingerprints, out var fingerprintName, out var fingerprintKey))
|
||||
{
|
||||
_logger.LogDebug($"Can't retrieve '{applicationName}' key, no fingerprint auth found");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
applicationKey = _keyManager.RetrieveKey(applicationName, fingerprintName, fingerprintKey);
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using Grpc.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Akari.Prototype.Server
|
||||
{
|
||||
public class GreeterService : Greeter.GreeterBase
|
||||
{
|
||||
private readonly ILogger<GreeterService> _logger;
|
||||
public GreeterService(ILogger<GreeterService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
|
||||
{
|
||||
return Task.FromResult(new HelloReply
|
||||
{
|
||||
Message = "Hello " + request.Name
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,14 @@ namespace Akari.Prototype.Server.Services
|
||||
|
||||
bool Contains(string applicationName);
|
||||
|
||||
bool IsFingerprintRegistered(string applicationName, string fingerprintName);
|
||||
|
||||
bool Remove(string applicationName);
|
||||
|
||||
bool AddFingerprint(string applicationName, string fingerprintName);
|
||||
|
||||
bool VerifyToken(string applicationName, string applicationToken);
|
||||
|
||||
bool TryRetrieveKey(string applicationName, string applicationToken, out AesGcm key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Akari.Prototype.Server.Models;
|
||||
|
||||
namespace Akari.Prototype.Server.Services
|
||||
{
|
||||
@@ -10,7 +11,7 @@ namespace Akari.Prototype.Server.Services
|
||||
{
|
||||
public bool AddFingerprint(string applicationName, string fingerprintName, AesGcm fingerprintKey);
|
||||
|
||||
public void Clear(string applicationName);
|
||||
public void Clear(Application application);
|
||||
|
||||
public bool Create(string applicationName);
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace Akari.Prototype.Server.Services
|
||||
public interface IMasterKeyService
|
||||
{
|
||||
public bool IsLoggedIn { get; }
|
||||
|
||||
public bool CheckConfig();
|
||||
public bool IsPasswordSet { get; }
|
||||
|
||||
public bool Login(string password);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Akari.Prototype.Server.Models;
|
||||
using Akari.Prototype.Server.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -58,12 +59,19 @@ namespace Akari.Prototype.Server.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear(string applicationName)
|
||||
public void Clear(Application application)
|
||||
{
|
||||
_logger.LogDebug($"Deleted keys for {applicationName}");
|
||||
// Do it safely to avoid deleting unwanted files
|
||||
File.Delete(GetMasterKeyPath(application.Name));
|
||||
|
||||
_logger.LogDebug($"Going to delete {GetKeyDirectoryPath(applicationName)}");
|
||||
//Directory.Delete(GetKeyDirectoryPath(applicationName));
|
||||
foreach (var fingerprint in application.Fingerprints)
|
||||
{
|
||||
File.Delete(GetKeyPath(application.Name, fingerprint));
|
||||
}
|
||||
|
||||
Directory.Delete(GetKeyDirectoryPath(application.Name), recursive: false);
|
||||
|
||||
_logger.LogDebug($"Deleted keys for {application.Name}");
|
||||
}
|
||||
|
||||
public bool Create(string applicationName)
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Akari.Prototype.Server.Services
|
||||
public const int MasterKeyLength = 256 / 8;
|
||||
|
||||
public bool IsLoggedIn => _key is not null;
|
||||
public bool IsPasswordSet => File.Exists(_akariPath.GetPath(ConfigPath));
|
||||
|
||||
private readonly ILogger<MasterKeyService> _logger;
|
||||
private readonly AkariPath _akariPath;
|
||||
@@ -33,8 +34,6 @@ namespace Akari.Prototype.Server.Services
|
||||
_akariPath = akariPath;
|
||||
}
|
||||
|
||||
public bool CheckConfig() => File.Exists(_akariPath.GetPath(ConfigPath));
|
||||
|
||||
public bool Login(string password)
|
||||
{
|
||||
if (_key is not null)
|
||||
@@ -81,7 +80,7 @@ namespace Akari.Prototype.Server.Services
|
||||
|
||||
public bool CreatePassword(string password)
|
||||
{
|
||||
if (CheckConfig())
|
||||
if (IsPasswordSet)
|
||||
{
|
||||
_logger.LogDebug("Tried to create password but it's already set.");
|
||||
|
||||
@@ -103,7 +102,7 @@ namespace Akari.Prototype.Server.Services
|
||||
|
||||
private bool LoadConfig()
|
||||
{
|
||||
if (!CheckConfig())
|
||||
if (!IsPasswordSet)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Akari.Prototype.Server
|
||||
services.AddSingleton<IAuthManager, AuthManager>();
|
||||
services.AddSingleton<IKeyManager, KeyManager>();
|
||||
services.AddSingleton<IApplicationsManager, ApplicationsManager>();
|
||||
services.AddSingleton<IMasterKeyService, MasterKeyService>();
|
||||
services.AddSingleton<AkariPath>();
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ namespace Akari.Prototype.Server
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapGrpcService<GreeterService>();
|
||||
endpoints.MapGrpcService<AkariService>();
|
||||
|
||||
endpoints.MapGet("/", async context =>
|
||||
{
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace Akari.Prototype.Server.Utils
|
||||
ansiConsole.WriteLine();
|
||||
|
||||
password = ansiConsole.Prompt(
|
||||
new TextPrompt<string>("Enter [green]master password[/]")
|
||||
new TextPrompt<string>("Enter [yellow]master password[/]")
|
||||
.PromptStyle("red")
|
||||
.Secret()
|
||||
);
|
||||
|
||||
ansiConsole.WriteLine();
|
||||
} while (!masterKeyService.Login(password) && tries < LoginMaxTries);
|
||||
|
||||
ansiConsole.WriteLine();
|
||||
|
||||
return masterKeyService.IsLoggedIn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Akari.Prototype.Server.Utils
|
||||
|
||||
// We write everything into one big array for easier encoding
|
||||
int encryptedDataLength = 4 + nonceSize + 4 + tagSize + cipherSize;
|
||||
Span<byte> encryptedData = encryptedDataLength < 1024
|
||||
Span<byte> encryptedData = encryptedDataLength <= 1024
|
||||
? stackalloc byte[encryptedDataLength]
|
||||
: new byte[encryptedDataLength];
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Akari.Prototype.Server.Utils
|
||||
var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);
|
||||
|
||||
// Decrypt
|
||||
Span<byte> plainBytes = cipherSize < 1024
|
||||
Span<byte> plainBytes = cipherSize <= 1024
|
||||
? stackalloc byte[cipherSize]
|
||||
: new byte[cipherSize];
|
||||
|
||||
|
||||
24
Akari.Prototype.Shared/Akari.Prototype.Shared.csproj
Normal file
24
Akari.Prototype.Shared/Akari.Prototype.Shared.csproj
Normal file
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.17.2" />
|
||||
<PackageReference Include="Grpc.Core.Api" Version="2.38.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.38.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Protos\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="**/*.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
37
Akari.Prototype.Shared/Protos/akari.proto
Normal file
37
Akari.Prototype.Shared/Protos/akari.proto
Normal file
@@ -0,0 +1,37 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "Akari.Prototype.Shared.Protos";
|
||||
|
||||
package akari;
|
||||
|
||||
service AkariApi {
|
||||
rpc Encrypt (EncryptRequest) returns (EncryptResponse);
|
||||
|
||||
rpc Decrypt (DecryptRequest) returns (DecryptResponse);
|
||||
}
|
||||
|
||||
message EncryptRequest {
|
||||
string application = 1;
|
||||
string token = 2;
|
||||
bytes plain = 3;
|
||||
}
|
||||
|
||||
message EncryptResponse {
|
||||
oneof response {
|
||||
string error_message = 1;
|
||||
bytes encrypted = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message DecryptRequest {
|
||||
string application = 1;
|
||||
string token = 2;
|
||||
bytes encrypted = 3;
|
||||
}
|
||||
|
||||
message DecryptResponse {
|
||||
oneof response {
|
||||
string error_message = 1;
|
||||
bytes plain = 2;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31321.278
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akari.Prototype.Server", "Akari.Prototype.Server\Akari.Prototype.Server.csproj", "{AFF5FC9F-41B5-4B05-953E-02DBC28833CD}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akari.Prototype.Server", "Akari.Prototype.Server\Akari.Prototype.Server.csproj", "{AFF5FC9F-41B5-4B05-953E-02DBC28833CD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akari.Prototype.Client", "Akari.Prototype.Client\Akari.Prototype.Client.csproj", "{3CEA1E09-B799-42B5-A258-A5E549FEC0ED}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akari.Prototype.Shared", "Akari.Prototype.Shared\Akari.Prototype.Shared.csproj", "{F0555B06-E172-490C-BF5D-AFA8D837358B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -15,6 +19,14 @@ Global
|
||||
{AFF5FC9F-41B5-4B05-953E-02DBC28833CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AFF5FC9F-41B5-4B05-953E-02DBC28833CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AFF5FC9F-41B5-4B05-953E-02DBC28833CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3CEA1E09-B799-42B5-A258-A5E549FEC0ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3CEA1E09-B799-42B5-A258-A5E549FEC0ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3CEA1E09-B799-42B5-A258-A5E549FEC0ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3CEA1E09-B799-42B5-A258-A5E549FEC0ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F0555B06-E172-490C-BF5D-AFA8D837358B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F0555B06-E172-490C-BF5D-AFA8D837358B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F0555B06-E172-490C-BF5D-AFA8D837358B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F0555B06-E172-490C-BF5D-AFA8D837358B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Reference in New Issue
Block a user