diff --git a/aliasvault.sln b/aliasvault.sln index ed67ff192..44122c490 100644 --- a/aliasvault.sln +++ b/aliasvault.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FaviconExtractor", "src\Uti EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AliasVault.UnitTests", "src\Tests\AliasVault.UnitTests\AliasVault.UnitTests.csproj", "{8E6A418A-B305-465D-857D-49953605C18E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cryptography", "src\Utilities\Cryptography\Cryptography.csproj", "{427EA8E2-EA76-467E-A6BC-201EFE40C0D0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AliasVault.Api", "src\AliasVault.Api\AliasVault.Api.csproj", "{B797C533-260E-4DA2-83B1-0EE4BCFE08DB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AliasVault.Client", "src\AliasVault.Client\AliasVault.Client.csproj", "{25248E01-5A4B-4F95-A63C-BEA01499A1C2}" @@ -55,6 +53,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TotpGenerator", "src\Utilit EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AliasVault.AuthLogging", "src\Utilities\AliasVault.AuthLogging\AliasVault.AuthLogging.csproj", "{DA175274-0FF7-4436-9266-742F96C2D1ED}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cryptography", "Cryptography", "{BB7E701E-B1C6-453E-800A-E12CE256318D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cryptography.Server", "src\Utilities\Cryptography\Cryptography.Server\Cryptography.Server.csproj", "{341EC443-0B6B-4E8C-AF46-D6156573CEA5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cryptography.Client", "src\Utilities\Cryptography\Cryptography.Client\Cryptography.Client.csproj", "{542C7B7D-C2B4-4AE3-9B2C-C62FCF4DFF8E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,10 +77,6 @@ Global {8E6A418A-B305-465D-857D-49953605C18E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E6A418A-B305-465D-857D-49953605C18E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E6A418A-B305-465D-857D-49953605C18E}.Release|Any CPU.Build.0 = Release|Any CPU - {427EA8E2-EA76-467E-A6BC-201EFE40C0D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427EA8E2-EA76-467E-A6BC-201EFE40C0D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427EA8E2-EA76-467E-A6BC-201EFE40C0D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427EA8E2-EA76-467E-A6BC-201EFE40C0D0}.Release|Any CPU.Build.0 = Release|Any CPU {B797C533-260E-4DA2-83B1-0EE4BCFE08DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B797C533-260E-4DA2-83B1-0EE4BCFE08DB}.Debug|Any CPU.Build.0 = Debug|Any CPU {B797C533-260E-4DA2-83B1-0EE4BCFE08DB}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -145,6 +145,14 @@ Global {DA175274-0FF7-4436-9266-742F96C2D1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA175274-0FF7-4436-9266-742F96C2D1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA175274-0FF7-4436-9266-742F96C2D1ED}.Release|Any CPU.Build.0 = Release|Any CPU + {341EC443-0B6B-4E8C-AF46-D6156573CEA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {341EC443-0B6B-4E8C-AF46-D6156573CEA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {341EC443-0B6B-4E8C-AF46-D6156573CEA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {341EC443-0B6B-4E8C-AF46-D6156573CEA5}.Release|Any CPU.Build.0 = Release|Any CPU + {542C7B7D-C2B4-4AE3-9B2C-C62FCF4DFF8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {542C7B7D-C2B4-4AE3-9B2C-C62FCF4DFF8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {542C7B7D-C2B4-4AE3-9B2C-C62FCF4DFF8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {542C7B7D-C2B4-4AE3-9B2C-C62FCF4DFF8E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -152,7 +160,6 @@ Global GlobalSection(NestedProjects) = preSolution {ED328644-A152-403D-86EB-81201AA07744} = {01AB9389-2F89-4F8E-A688-BF4BF1FC42C8} {8E6A418A-B305-465D-857D-49953605C18E} = {29DE523D-EEF2-41E9-AC12-F20D8D02BEBB} - {427EA8E2-EA76-467E-A6BC-201EFE40C0D0} = {01AB9389-2F89-4F8E-A688-BF4BF1FC42C8} {AF013D08-1BF6-4E23-87D2-37F614BE7952} = {29DE523D-EEF2-41E9-AC12-F20D8D02BEBB} {1277105D-50CD-4CE0-9C2C-549F46867E54} = {5F7417F6-4388-49CC-9511-ED63C4A6488A} {FE10F294-817F-477E-A24F-8597A15AF0B5} = {5F7417F6-4388-49CC-9511-ED63C4A6488A} @@ -167,6 +174,9 @@ Global {951C3DF8-DF22-4B2B-839F-FBA26DDD8ABD} = {01AB9389-2F89-4F8E-A688-BF4BF1FC42C8} {E8D9C551-67D2-4651-8EDF-4262DF7375CE} = {01AB9389-2F89-4F8E-A688-BF4BF1FC42C8} {DA175274-0FF7-4436-9266-742F96C2D1ED} = {01AB9389-2F89-4F8E-A688-BF4BF1FC42C8} + {BB7E701E-B1C6-453E-800A-E12CE256318D} = {01AB9389-2F89-4F8E-A688-BF4BF1FC42C8} + {341EC443-0B6B-4E8C-AF46-D6156573CEA5} = {BB7E701E-B1C6-453E-800A-E12CE256318D} + {542C7B7D-C2B4-4AE3-9B2C-C62FCF4DFF8E} = {BB7E701E-B1C6-453E-800A-E12CE256318D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FEE82475-C009-4762-8113-A6563D9DC49E} diff --git a/src/AliasVault.Admin/AliasVault.Admin.csproj b/src/AliasVault.Admin/AliasVault.Admin.csproj index e83a10531..960a0c95f 100644 --- a/src/AliasVault.Admin/AliasVault.Admin.csproj +++ b/src/AliasVault.Admin/AliasVault.Admin.csproj @@ -46,6 +46,6 @@ - + diff --git a/src/AliasVault.Admin/Program.cs b/src/AliasVault.Admin/Program.cs index 1f89236f0..793b3eb4e 100644 --- a/src/AliasVault.Admin/Program.cs +++ b/src/AliasVault.Admin/Program.cs @@ -5,7 +5,6 @@ // //----------------------------------------------------------------------- -using System.Data.Common; using System.Globalization; using System.Reflection; using AliasServerDb; @@ -16,10 +15,9 @@ using AliasVault.Admin.Main; using AliasVault.Admin.Services; using AliasVault.AuthLogging; using AliasVault.Logging; -using Cryptography; +using Cryptography.Server; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Identity; -using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); diff --git a/src/AliasVault.Api/AliasVault.Api.csproj b/src/AliasVault.Api/AliasVault.Api.csproj index a3bb49e3b..2434c7650 100644 --- a/src/AliasVault.Api/AliasVault.Api.csproj +++ b/src/AliasVault.Api/AliasVault.Api.csproj @@ -37,7 +37,8 @@ - + + diff --git a/src/AliasVault.Api/Controllers/AuthController.cs b/src/AliasVault.Api/Controllers/AuthController.cs index 1809c2210..506234c55 100644 --- a/src/AliasVault.Api/Controllers/AuthController.cs +++ b/src/AliasVault.Api/Controllers/AuthController.cs @@ -18,7 +18,8 @@ using AliasVault.Shared.Models.WebApi; using AliasVault.Shared.Models.WebApi.Auth; using AliasVault.Shared.Providers.Time; using Asp.Versioning; -using Cryptography.Models; +using Cryptography.Client; +using Cryptography.Client.Models; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -84,7 +85,7 @@ public class AuthController(IDbContextFactory dbContextFac } // Server creates ephemeral and sends to client - var ephemeral = Cryptography.Srp.GenerateEphemeralServer(user.Verifier); + var ephemeral = Srp.GenerateEphemeralServer(user.Verifier); // Store the server ephemeral in memory cache for Validate() endpoint to use. cache.Set(model.Username, ephemeral.Secret, TimeSpan.FromMinutes(5)); @@ -461,7 +462,7 @@ public class AuthController(IDbContextFactory dbContextFac return null; } - var serverSession = Cryptography.Srp.DeriveSessionServer( + var serverSession = Srp.DeriveSessionServer( serverSecretEphemeral.ToString() ?? string.Empty, clientEphemeral, user.Salt, diff --git a/src/AliasVault.Api/Program.cs b/src/AliasVault.Api/Program.cs index db22e9692..5e37d8e89 100644 --- a/src/AliasVault.Api/Program.cs +++ b/src/AliasVault.Api/Program.cs @@ -14,10 +14,9 @@ using AliasVault.AuthLogging; using AliasVault.Logging; using AliasVault.Shared.Providers.Time; using Asp.Versioning; -using Cryptography; +using Cryptography.Server; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; diff --git a/src/AliasVault.Client/AliasVault.Client.csproj b/src/AliasVault.Client/AliasVault.Client.csproj index 84b88b773..d3fa8b5ea 100644 --- a/src/AliasVault.Client/AliasVault.Client.csproj +++ b/src/AliasVault.Client/AliasVault.Client.csproj @@ -77,7 +77,7 @@ - + diff --git a/src/AliasVault.Client/Auth/Pages/Login.razor b/src/AliasVault.Client/Auth/Pages/Login.razor index 296583b80..e89d4c0b3 100644 --- a/src/AliasVault.Client/Auth/Pages/Login.razor +++ b/src/AliasVault.Client/Auth/Pages/Login.razor @@ -5,7 +5,7 @@ @using System.Text.Json @using AliasVault.Shared.Models.WebApi.Auth @using AliasVault.Client.Auth.Components -@using Cryptography +@using Cryptography.Client @using SecureRemotePassword @if (ShowTwoFactorAuthStep) diff --git a/src/AliasVault.Client/Auth/Pages/Register.razor b/src/AliasVault.Client/Auth/Pages/Register.razor index aad447913..1a3f430d3 100644 --- a/src/AliasVault.Client/Auth/Pages/Register.razor +++ b/src/AliasVault.Client/Auth/Pages/Register.razor @@ -9,7 +9,7 @@ @using AliasVault.Shared.Models.WebApi.Auth @using AliasVault.Client.Auth.Components @using AliasVault.Client.Auth.Pages.Base -@using Cryptography +@using Cryptography.Client @using SecureRemotePassword

@@ -71,7 +71,7 @@ byte[] passwordHash = await Encryption.DeriveKeyFromPasswordAsync(RegisterModel.Password, salt); var passwordHashString = BitConverter.ToString(passwordHash).Replace("-", string.Empty); - var srpSignup = Cryptography.Srp.SignupPrepareAsync(client, salt, RegisterModel.Username, passwordHashString); + var srpSignup = Srp.SignupPrepareAsync(client, salt, RegisterModel.Username, passwordHashString); var result = await Http.PostAsJsonAsync("api/v1/Auth/register", srpSignup); var responseContent = await result.Content.ReadAsStringAsync(); diff --git a/src/AliasVault.Client/Auth/Pages/Unlock.razor b/src/AliasVault.Client/Auth/Pages/Unlock.razor index b958a776e..90cf4ad25 100644 --- a/src/AliasVault.Client/Auth/Pages/Unlock.razor +++ b/src/AliasVault.Client/Auth/Pages/Unlock.razor @@ -2,10 +2,9 @@ @inherits AliasVault.Client.Auth.Pages.Base.LoginBase @layout Auth.Layout.MainLayout @using System.Text.Json -@using AliasVault.Shared.Models @using AliasVault.Client.Auth.Components @using AliasVault.Shared.Models.WebApi.Auth -@using Cryptography +@using Cryptography.Client
Bonnie image diff --git a/src/AliasVault.Client/wwwroot/index.template.html b/src/AliasVault.Client/wwwroot/index.template.html index 8eea190e3..f6f16b9af 100644 --- a/src/AliasVault.Client/wwwroot/index.template.html +++ b/src/AliasVault.Client/wwwroot/index.template.html @@ -104,10 +104,10 @@ window.addEventListener('load', manageLoadingScreen); - - - - + + + + diff --git a/src/Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj b/src/Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj index 7b6060041..8770ad422 100644 --- a/src/Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj +++ b/src/Services/AliasVault.SmtpService/AliasVault.SmtpService.csproj @@ -38,6 +38,6 @@ - + diff --git a/src/Services/AliasVault.SmtpService/Handlers/DatabaseMessageStore.cs b/src/Services/AliasVault.SmtpService/Handlers/DatabaseMessageStore.cs index b7e780300..2c7923190 100644 --- a/src/Services/AliasVault.SmtpService/Handlers/DatabaseMessageStore.cs +++ b/src/Services/AliasVault.SmtpService/Handlers/DatabaseMessageStore.cs @@ -11,7 +11,7 @@ using System.Buffers; using System.Net.Mail; using System.Text.RegularExpressions; using AliasServerDb; -using Cryptography; +using Cryptography.Server; using Microsoft.EntityFrameworkCore; using MimeKit; using NUglify; diff --git a/src/Tests/AliasVault.IntegrationTests/AliasVault.IntegrationTests.csproj b/src/Tests/AliasVault.IntegrationTests/AliasVault.IntegrationTests.csproj index ba01ea170..cbd8e98b4 100644 --- a/src/Tests/AliasVault.IntegrationTests/AliasVault.IntegrationTests.csproj +++ b/src/Tests/AliasVault.IntegrationTests/AliasVault.IntegrationTests.csproj @@ -44,6 +44,7 @@ + diff --git a/src/Tests/AliasVault.IntegrationTests/SmtpServer/SmtpServerTests.cs b/src/Tests/AliasVault.IntegrationTests/SmtpServer/SmtpServerTests.cs index 1a03ad5e9..cdc2a4af1 100644 --- a/src/Tests/AliasVault.IntegrationTests/SmtpServer/SmtpServerTests.cs +++ b/src/Tests/AliasVault.IntegrationTests/SmtpServer/SmtpServerTests.cs @@ -9,7 +9,7 @@ namespace AliasVault.IntegrationTests.SmtpServer; using System.Text; using AliasServerDb; -using Cryptography; +using Cryptography.Server; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.EntityFrameworkCore; diff --git a/src/Tests/AliasVault.UnitTests/AliasVault.UnitTests.csproj b/src/Tests/AliasVault.UnitTests/AliasVault.UnitTests.csproj index 48194f5f5..5f2874c67 100644 --- a/src/Tests/AliasVault.UnitTests/AliasVault.UnitTests.csproj +++ b/src/Tests/AliasVault.UnitTests/AliasVault.UnitTests.csproj @@ -52,7 +52,8 @@ - + + diff --git a/src/Tests/AliasVault.UnitTests/Utilities/RsaEncryptionTests.cs b/src/Tests/AliasVault.UnitTests/Utilities/RsaEncryptionTests.cs index fe5bb0b0e..d5d732e8a 100644 --- a/src/Tests/AliasVault.UnitTests/Utilities/RsaEncryptionTests.cs +++ b/src/Tests/AliasVault.UnitTests/Utilities/RsaEncryptionTests.cs @@ -8,7 +8,7 @@ namespace AliasVault.Tests.Utilities; using System.Text.Json; -using Cryptography; +using Cryptography.Server; /// /// Tests for the SrpArgonEncryption class. diff --git a/src/Tests/AliasVault.UnitTests/Utilities/SrpArgonEncryptionTests.cs b/src/Tests/AliasVault.UnitTests/Utilities/SrpArgonEncryptionTests.cs index d1249f281..4cb7f8155 100644 --- a/src/Tests/AliasVault.UnitTests/Utilities/SrpArgonEncryptionTests.cs +++ b/src/Tests/AliasVault.UnitTests/Utilities/SrpArgonEncryptionTests.cs @@ -7,7 +7,7 @@ namespace AliasVault.Tests.Utilities; -using Cryptography; +using Cryptography.Client; using Org.BouncyCastle.Crypto; using SecureRemotePassword; @@ -29,11 +29,11 @@ public class SrpArgonEncryptionTests string plaintext = "Hello, World!"; // Derive a key from the password using Argon2id - byte[] key = await Encryption.DeriveKeyFromPasswordAsync(password, salt); + byte[] key = await Cryptography.Client.Encryption.DeriveKeyFromPasswordAsync(password, salt); Console.WriteLine($"Derived key: {key.Length} bytes (hex: {BitConverter.ToString(key).Replace("-", string.Empty)})"); // SymmetricEncrypt the plaintext - string encrypted = Encryption.SymmetricEncrypt(plaintext, key); + string encrypted = Cryptography.Server.Encryption.SymmetricEncrypt(plaintext, key); Console.WriteLine($"Encrypted: {encrypted}"); Assert.That(encrypted, Is.Not.Null); @@ -41,7 +41,7 @@ public class SrpArgonEncryptionTests Assert.That(encrypted, Is.Not.EqualTo(plaintext)); // SymmetricDecrypt the ciphertext - string decrypted = Encryption.SymmetricDecrypt(encrypted, key); + string decrypted = Cryptography.Server.Encryption.SymmetricDecrypt(encrypted, key); Console.WriteLine($"Decrypted: {decrypted}"); Assert.That(decrypted, Is.EqualTo(plaintext)); } @@ -57,7 +57,7 @@ public class SrpArgonEncryptionTests var encryptionKey = Convert.FromBase64String(jsEncryptionKeyBase64); // Try to decrypt it from .NET. - var decrypted = Encryption.SymmetricDecrypt(jsEncryptedBase64Contents, encryptionKey); + var decrypted = Cryptography.Server.Encryption.SymmetricDecrypt(jsEncryptedBase64Contents, encryptionKey); // Assert that its equal as the original what we expect. var originalUnencrypted = "U1FMaXRlIGZvcm1hdCAzABAAAQEAQCAgAAAAAQAAAAMAAAAAAAAAAAAAAAIAAAAEAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAC52iQ0P+AACDvUADvUPxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFNAQcXHx8Bgml0YWJsZVBhc3N3b3Jkc1Bhc3N3b3JkcwJDUkVBVEUgVEFCTEUgIlBhc3N3b3JkcyIgKAogICAgIklkIiBURVhUIE5PVCBOVUxMIENPTlNUUkFJTlQgIlBLX1Bhc3N3b3JkcyIgUFJJTUFSWSBLRVksCiAgICAiVmFsdWUiIFRFWFQgTlVMTCwKICAgICJDcmVhdGVkQXQiIFRFWFQgTk9UIE5VTEwsCiAgICAiVXBkYXRlZEF0IiBURVhUIE5PVCBOVUxMCikxAgYXRR8BAGluZGV4c3FsaXRlX2F1dG9pbmRleF9QYXNzd29yZHNfMVBhc3N3b3JkcwMAAAAIAAAAAA0AAAADDrMAD5EPIg6zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgDVQFFOTc4MDJEQi05RUIzLTRDNkItQTdCOS03NDZFQ0YzNzQ4ODgCJwNVCTY5NjNCNUU5LURBNUUtNDgwMC05QzIwLTNEMDk3QkVCQjQyMigDVQE1NUMzM0VDNi1DQ0M4LTRBMjUtQjgyOC01MkJBNDlFQTVEMTcD"; @@ -77,15 +77,15 @@ public class SrpArgonEncryptionTests string plaintext = "Hello, World!"; // Derive a key from the password using Argon2id - byte[] key = await Encryption.DeriveKeyFromPasswordAsync(password, salt); + byte[] key = await Cryptography.Client.Encryption.DeriveKeyFromPasswordAsync(password, salt); // SymmetricEncrypt the plaintext - string encrypted = Encryption.SymmetricEncrypt(plaintext, key); + string encrypted = Cryptography.Server.Encryption.SymmetricEncrypt(plaintext, key); // SymmetricDecrypt the ciphertext using a different key - byte[] key2 = await Encryption.DeriveKeyFromPasswordAsync("your-password2", salt); + byte[] key2 = await Cryptography.Client.Encryption.DeriveKeyFromPasswordAsync("your-password2", salt); - Assert.Throws(() => Encryption.SymmetricDecrypt(encrypted, key2)); + Assert.Throws(() => Cryptography.Server.Encryption.SymmetricDecrypt(encrypted, key2)); } /// @@ -103,7 +103,7 @@ public class SrpArgonEncryptionTests var client = new SrpClient(); var salt = client.GenerateSalt(); - byte[] passwordHash = await Encryption.DeriveKeyFromPasswordAsync(password, salt); + byte[] passwordHash = await Cryptography.Client.Encryption.DeriveKeyFromPasswordAsync(password, salt); var passwordHashString = BitConverter.ToString(passwordHash).Replace("-", string.Empty); var srpSignup = Srp.SignupPrepareAsync(client, salt, email, passwordHashString); @@ -151,21 +151,21 @@ public class SrpArgonEncryptionTests string salt = "your-salt"; // Derive a key from the password using Argon2id - byte[] key = await Encryption.DeriveKeyFromPasswordAsync(password, salt); + byte[] key = await Cryptography.Client.Encryption.DeriveKeyFromPasswordAsync(password, salt); // Generate random byte array of 1000 bytes byte[] plainBytes = new byte[1000]; new Random().NextBytes(plainBytes); // SymmetricEncrypt the plainBytes - var cipherBytes = Encryption.SymmetricEncrypt(plainBytes, key); + var cipherBytes = Cryptography.Server.Encryption.SymmetricEncrypt(plainBytes, key); Assert.That(cipherBytes, Is.Not.Null); Assert.That(cipherBytes, Is.Not.Empty); Assert.That(cipherBytes, Is.Not.EqualTo(plainBytes)); // SymmetricDecrypt the cipherBytes - var decrypted = Encryption.SymmetricDecrypt(cipherBytes, key); + var decrypted = Cryptography.Server.Encryption.SymmetricDecrypt(cipherBytes, key); Assert.That(decrypted, Is.EqualTo(plainBytes)); } } diff --git a/src/Utilities/Cryptography/Cryptography.Client/Cryptography.Client.csproj b/src/Utilities/Cryptography/Cryptography.Client/Cryptography.Client.csproj new file mode 100644 index 000000000..a03387865 --- /dev/null +++ b/src/Utilities/Cryptography/Cryptography.Client/Cryptography.Client.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + enable + enable + + + + bin\Debug\net8.0\CryptographyClient.xml + true + + + + bin\Release\net8.0\CryptographyClient.xml + true + + + + + stylecop.json + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/Utilities/Cryptography/Cryptography.Client/Encryption.cs b/src/Utilities/Cryptography/Cryptography.Client/Encryption.cs new file mode 100644 index 000000000..898d16521 --- /dev/null +++ b/src/Utilities/Cryptography/Cryptography.Client/Encryption.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) lanedirt. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// +//----------------------------------------------------------------------- + +namespace Cryptography.Client; + +using System.Text; +using Konscious.Security.Cryptography; + +/// +/// RSA/AES and Argon2id encryption methods. +/// +public static class Encryption +{ + /// + /// Derive a key used for encryption/decryption based on a user password and system salt. + /// + /// User password. + /// The salt to use for the Argon2id hash. + /// SrpArgonEncryption key as byte array. + public static async Task DeriveKeyFromPasswordAsync(string password, string salt) + { + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + byte[] saltBytes = Encoding.UTF8.GetBytes(salt); + + var argon2 = new Argon2id(passwordBytes) + { + Salt = saltBytes, + DegreeOfParallelism = 4, + MemorySize = 8192, + Iterations = 1, + }; + + return await argon2.GetBytesAsync(32); // Generate a 256-bit key + } +} diff --git a/src/Utilities/Cryptography/Models/SrpSignup.cs b/src/Utilities/Cryptography/Cryptography.Client/Models/SrpSignup.cs similarity index 97% rename from src/Utilities/Cryptography/Models/SrpSignup.cs rename to src/Utilities/Cryptography/Cryptography.Client/Models/SrpSignup.cs index 954fb3cf8..d5ead9120 100644 --- a/src/Utilities/Cryptography/Models/SrpSignup.cs +++ b/src/Utilities/Cryptography/Cryptography.Client/Models/SrpSignup.cs @@ -5,7 +5,7 @@ // //----------------------------------------------------------------------- -namespace Cryptography.Models; +namespace Cryptography.Client.Models; /// /// Represents the data required for signing up with the Secure Remote Password (SRP) protocol. diff --git a/src/Utilities/Cryptography/Srp.cs b/src/Utilities/Cryptography/Cryptography.Client/Srp.cs similarity index 98% rename from src/Utilities/Cryptography/Srp.cs rename to src/Utilities/Cryptography/Cryptography.Client/Srp.cs index 14a12cbc7..3caa9071c 100644 --- a/src/Utilities/Cryptography/Srp.cs +++ b/src/Utilities/Cryptography/Cryptography.Client/Srp.cs @@ -5,9 +5,9 @@ // //----------------------------------------------------------------------- -namespace Cryptography; +namespace Cryptography.Client; -using Cryptography.Models; +using Cryptography.Client.Models; using SecureRemotePassword; /// diff --git a/src/Utilities/Cryptography/CertificateGenerator.cs b/src/Utilities/Cryptography/Cryptography.Server/CertificateGenerator.cs similarity index 98% rename from src/Utilities/Cryptography/CertificateGenerator.cs rename to src/Utilities/Cryptography/Cryptography.Server/CertificateGenerator.cs index a8a2eb63d..ea6a0fcf3 100644 --- a/src/Utilities/Cryptography/CertificateGenerator.cs +++ b/src/Utilities/Cryptography/Cryptography.Server/CertificateGenerator.cs @@ -5,7 +5,7 @@ // //----------------------------------------------------------------------- -namespace Cryptography; +namespace Cryptography.Server; using System; using System.IO; diff --git a/src/Utilities/Cryptography/Cryptography.csproj b/src/Utilities/Cryptography/Cryptography.Server/Cryptography.Server.csproj similarity index 77% rename from src/Utilities/Cryptography/Cryptography.csproj rename to src/Utilities/Cryptography/Cryptography.Server/Cryptography.Server.csproj index ebab5f520..d8b847017 100644 --- a/src/Utilities/Cryptography/Cryptography.csproj +++ b/src/Utilities/Cryptography/Cryptography.Server/Cryptography.Server.csproj @@ -17,13 +17,11 @@ - + - - all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -31,7 +29,7 @@ - + diff --git a/src/Utilities/Cryptography/DataProtectionExtensions.cs b/src/Utilities/Cryptography/Cryptography.Server/DataProtectionExtensions.cs similarity index 98% rename from src/Utilities/Cryptography/DataProtectionExtensions.cs rename to src/Utilities/Cryptography/Cryptography.Server/DataProtectionExtensions.cs index 829a09030..602a628c8 100644 --- a/src/Utilities/Cryptography/DataProtectionExtensions.cs +++ b/src/Utilities/Cryptography/Cryptography.Server/DataProtectionExtensions.cs @@ -5,7 +5,7 @@ // //----------------------------------------------------------------------- -namespace Cryptography; +namespace Cryptography.Server; using System.Security.Cryptography.X509Certificates; using AliasServerDb; diff --git a/src/Utilities/Cryptography/EmailEncryption.cs b/src/Utilities/Cryptography/Cryptography.Server/EmailEncryption.cs similarity index 99% rename from src/Utilities/Cryptography/EmailEncryption.cs rename to src/Utilities/Cryptography/Cryptography.Server/EmailEncryption.cs index b08af670e..343de0de1 100644 --- a/src/Utilities/Cryptography/EmailEncryption.cs +++ b/src/Utilities/Cryptography/Cryptography.Server/EmailEncryption.cs @@ -5,7 +5,7 @@ // //----------------------------------------------------------------------- -namespace Cryptography; +namespace Cryptography.Server; using AliasServerDb; diff --git a/src/Utilities/Cryptography/Encryption.cs b/src/Utilities/Cryptography/Cryptography.Server/Encryption.cs similarity index 90% rename from src/Utilities/Cryptography/Encryption.cs rename to src/Utilities/Cryptography/Cryptography.Server/Encryption.cs index 446d68adf..ffafc1197 100644 --- a/src/Utilities/Cryptography/Encryption.cs +++ b/src/Utilities/Cryptography/Cryptography.Server/Encryption.cs @@ -5,12 +5,11 @@ // //----------------------------------------------------------------------- -namespace Cryptography; +namespace Cryptography.Server; using System.Security.Cryptography; using System.Text; using System.Text.Json; -using Konscious.Security.Cryptography; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; @@ -68,28 +67,6 @@ public static class Encryption } } - /// - /// Derive a key used for encryption/decryption based on a user password and system salt. - /// - /// User password. - /// The salt to use for the Argon2id hash. - /// SrpArgonEncryption key as byte array. - public static async Task DeriveKeyFromPasswordAsync(string password, string salt) - { - byte[] passwordBytes = Encoding.UTF8.GetBytes(password); - byte[] saltBytes = Encoding.UTF8.GetBytes(salt); - - var argon2 = new Argon2id(passwordBytes) - { - Salt = saltBytes, - DegreeOfParallelism = 4, - MemorySize = 8192, - Iterations = 1, - }; - - return await argon2.GetBytesAsync(32); // Generate a 256-bit key - } - /// /// SymmetricEncrypt a plaintext string using AES-256 GCM. ///