aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-01-14 16:32:04 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-01-14 16:32:04 -0500
commit1ce9119c2571b1e03f7e7b69fb3ef2e63ade97a6 (patch)
tree11ea3fafdc1f7e88319a3f3a174c2a89b69dbd48
parent551066ed9a255bd47c1c5789ec1998fda64bd5aa (diff)
AccountUtil + client token upgrade
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs8
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj2
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs6
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs25
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs10
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs6
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs8
-rw-r--r--plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj4
-rw-r--r--plugins/VNLib.Plugins.Essentials.Content.Routing/src/RouteComparer.cs2
-rw-r--r--plugins/VNLib.Plugins.Essentials.Content.Routing/src/Router.cs2
-rw-r--r--plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientAccessTokenState.cs2
-rw-r--r--plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientRequestState.cs4
-rw-r--r--plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs10
13 files changed, 64 insertions, 25 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs
index 5c22344..c39165c 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/Endpoints/RegistrationEntpoint.cs
@@ -47,7 +47,7 @@ using VNLib.Plugins.Extensions.Loading.Users;
using VNLib.Plugins.Extensions.Validation;
using VNLib.Plugins.Extentions.TransactionalEmail;
using VNLib.Plugins.Essentials.Accounts.Registration.TokenRevocation;
-using static VNLib.Plugins.Essentials.Accounts.AccountManager;
+using static VNLib.Plugins.Essentials.Accounts.AccountUtil;
namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints
@@ -73,7 +73,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints
private readonly TimeSpan RegExpiresSec;
/// <summary>
- /// Creates back-end functionality for a "registration" or "sign-up" page that integrates with the <see cref="AccountManager"/> plugin
+ /// Creates back-end functionality for a "registration" or "sign-up" page that integrates with the <see cref="AccountUtil"/> plugin
/// </summary>
/// <param name="Path">The path identifier</param>
/// <exception cref="ArgumentException"></exception>
@@ -186,7 +186,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints
DateTimeOffset iat = DateTimeOffset.FromUnixTimeSeconds(reg.RootElement.GetProperty("iat").GetInt64());
//Verify IAT against expiration at second resolution
- if (webm.Assert(iat.Add(RegExpiresSec) > DateTimeOffset.UtcNow, FAILED_AUTH_ERR))
+ if (webm.Assert(iat.Add(RegExpiresSec) > entity.RequestedTimeUtc, FAILED_AUTH_ERR))
{
entity.CloseResponse(webm);
return VfReturnType.VirtualSkip;
@@ -273,7 +273,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Registration.Endpoints
}
//Get exact timestamp
- DateTimeOffset timeStamp = DateTimeOffset.UtcNow;
+ DateTimeOffset timeStamp = entity.RequestedTimeUtc;
//generate random nonce for entropy
string entropy = EntropyNonce;
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj
index 981d252..bdd9fde 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj
+++ b/plugins/VNLib.Plugins.Essentials.Accounts.Registration/src/VNLib.Plugins.Essentials.Accounts.Registration.csproj
@@ -36,7 +36,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\core\lib\Plugins.Essentials\src\VNLib.Plugins.Essentials.csproj" />
<ProjectReference Include="..\..\..\..\Emails.Transactional\lib\Emails.Transactional.Client\src\Emails.Transactional.Client.csproj" />
- <ProjectReference Include="..\..\..\..\Emails.Transactional\lib\Emails.Transactional.Extensions\src\VNLib.Plugins.Extentions.TransactionalEmail.csproj" />
+ <ProjectReference Include="..\..\..\..\Emails.Transactional\lib\Emails.Transactional.Extensions\src\Emails.Transactional.Client.Extensions.csproj" />
<ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Data\src\VNLib.Plugins.Extensions.Data.csproj" />
<ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Loading.Sql\src\VNLib.Plugins.Extensions.Loading.Sql.csproj" />
<ProjectReference Include="..\..\..\..\Extensions\lib\VNLib.Plugins.Extensions.Loading\src\VNLib.Plugins.Extensions.Loading.csproj" />
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs
index ed79476..f4401a9 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/AccountsEntryPoint.cs
@@ -110,18 +110,18 @@ namespace VNLib.Plugins.Essentials.Accounts
return;
}
string username = args[uid + 1].Trim();
- string randomUserId = AccountManager.GetRandomUserId();
+ string randomUserId = AccountUtil.GetRandomUserId();
//Password as privatestring DANGEROUS to refs
using (PrivateString password = (PrivateString)args[pwd + 1].Trim()!)
{
//Hash the password
using PrivateString passHash = Passwords.Hash(password);
//Create the user
- using IUser user = await Users.CreateUserAsync(randomUserId, username, AccountManager.MINIMUM_LEVEL, passHash);
+ using IUser user = await Users.CreateUserAsync(randomUserId, username, AccountUtil.MINIMUM_LEVEL, passHash);
//Set active flag
user.Status = UserStatus.Active;
//Set local account
- user.SetAccountOrigin(AccountManager.LOCAL_ACCOUNT_ORIGIN);
+ user.SetAccountOrigin(AccountUtil.LOCAL_ACCOUNT_ORIGIN);
await user.ReleaseAsync();
}
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs
index fe5a65b..0ff0869 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/KeepAliveEndpoint.cs
@@ -27,14 +27,19 @@ using System.Net;
using System.Text.Json;
using System.Collections.Generic;
+using VNLib.Utils.Extensions;
using VNLib.Plugins.Essentials.Endpoints;
+using VNLib.Plugins.Essentials.Extensions;
using VNLib.Plugins.Extensions.Loading;
+
namespace VNLib.Plugins.Essentials.Accounts.Endpoints
{
[ConfigurationName("keepalive_endpoint")]
internal sealed class KeepAliveEndpoint : ProtectedWebEndpoint
{
+ readonly TimeSpan tokenRegenTime;
+
/*
* Endpoint does not use a log, so IniPathAndLog is never called
* and path verification happens verbosly
@@ -43,6 +48,8 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
{
string? path = config["path"].GetString();
+ tokenRegenTime = config["token_refresh_sec"].GetTimeSpan(TimeParseType.Seconds);
+
InitPathAndLog(path, pbase.Log);
}
@@ -56,6 +63,24 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
//Allow post to update user's credentials
protected override VfReturnType Post(HttpEntity entity)
{
+ //Get the last token update
+ DateTimeOffset lastTokenUpdate = entity.Session.LastTokenUpgrade();
+
+ //See if its expired
+ if (lastTokenUpdate.Add(tokenRegenTime) < entity.RequestedTimeUtc)
+ {
+ //if so updaet token
+ WebMessage webm = new()
+ {
+ Token = entity.RegenerateClientToken(),
+ Success = true
+ };
+
+ //Send the update message to the client
+ entity.CloseResponse(webm);
+ return VfReturnType.VirtualSkip;
+ }
+
//Return okay
entity.CloseResponse(HttpStatusCode.OK);
return VfReturnType.VirtualSkip;
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs
index 4100620..f973fe8 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/LoginEndpoint.cs
@@ -44,7 +44,7 @@ using VNLib.Plugins.Essentials.Accounts.Validators;
using VNLib.Plugins.Extensions.Loading;
using VNLib.Plugins.Extensions.Loading.Users;
using static VNLib.Plugins.Essentials.Statics;
-using static VNLib.Plugins.Essentials.Accounts.AccountManager;
+using static VNLib.Plugins.Essentials.Accounts.AccountUtil;
namespace VNLib.Plugins.Essentials.Accounts.Endpoints
@@ -378,11 +378,14 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
private static string EncryptSecret(string pubKey, byte[] secret)
{
//Alloc buffer for secret
- using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(4096);
+ using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(4096);
+
//Try to encrypt the data
ERRNO count = TryEncryptClientData(pubKey, secret, buffer.Span);
+
//Clear secret
RandomHash.GetRandomBytes(secret);
+
//Convert to base64 string
return Convert.ToBase64String(buffer.Span[..(int)count]);
}
@@ -391,11 +394,13 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
{
//Recover last counter value
TimestampedCounter flc = user.FailedLoginCount();
+
if(flc.Count < MaxFailedLogins)
{
//Period exceeded
return false;
}
+
//See if the flc timeout period has expired
if (flc.LastModified.Add(FailedCountTimeout) < DateTimeOffset.UtcNow)
{
@@ -403,6 +408,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
user.FailedLoginCount(0);
return false;
}
+
//Count has been exceeded, and has not timed out yet
return true;
}
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs
index 6ebb024..df20084 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/Endpoints/MFAEndpoint.cs
@@ -162,11 +162,13 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
//generate a new secret (passing the buffer which will get copied to an array because the pw bytes can be modified during encryption)
byte[] secretBuffer = user.MFAGenreateTOTPSecret(MultiFactor);
//Alloc output buffer
- UnsafeMemoryHandle<byte> outputBuffer = Memory.UnsafeAlloc<byte>(4096, true);
+ UnsafeMemoryHandle<byte> outputBuffer = MemoryUtil.UnsafeAlloc<byte>(4096, true);
+
try
{
//Encrypt the secret for the client
ERRNO count = entity.Session.TryEncryptClientData(secretBuffer, outputBuffer.Span);
+
if (!count)
{
webm.Result = "There was an error updating your credentials";
@@ -174,6 +176,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
Log.Warn("TOTP secret encryption failed, for requested user {uid}", entity.Session.UserID);
break;
}
+
webm.Result = new TOTPUpdateMessage()
{
Issuer = MultiFactor.IssuerName,
@@ -183,6 +186,7 @@ namespace VNLib.Plugins.Essentials.Accounts.Endpoints
//Convert the secret to base64 string to send to client
Base64EncSecret = Convert.ToBase64String(outputBuffer.Span[..(int)count])
};
+
//set success flag
webm.Success = true;
}
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs
index 1ec9953..ee623e2 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/MFA/UserMFAExtensions.cs
@@ -112,7 +112,7 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA
return false;
}
//Alloc buffer with zero o
- using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(base32Secret.Length, true);
+ using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(base32Secret.Length, true);
ERRNO count = VnEncoding.TryFromBase32Chars(base32Secret, buffer);
//Verify the TOTP using the decrypted secret
return count && VerifyTOTP(code, buffer.AsSpan(0, count), config);
@@ -273,9 +273,11 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA
//Verifies a jwt stored signature against the actual signature
static bool VerifyStoredSig(ReadOnlySpan<char> base64string, ReadOnlySpan<byte> signature)
{
- using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(base64string.Length, true);
+ using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(base64string.Length, true);
+
//Recover base64
ERRNO count = VnEncoding.TryFromBase64Chars(base64string, buffer.Span);
+
//Compare
return CryptographicOperations.FixedTimeEquals(signature, buffer.Span[..(int)count]);
}
@@ -300,8 +302,10 @@ namespace VNLib.Plugins.Essentials.Accounts.MFA
//get request body
using JsonDocument doc = jwt.GetPayload();
+
//Recover issued at time
DateTimeOffset iat = DateTimeOffset.FromUnixTimeMilliseconds(doc.RootElement.GetProperty("iat").GetInt64());
+
//Verify its not timed out
if (iat.Add(config.UpgradeValidFor) < DateTimeOffset.UtcNow)
{
diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj b/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj
index 98ba1ab..f7c7909 100644
--- a/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj
+++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/VNLib.Plugins.Essentials.Accounts.csproj
@@ -5,7 +5,7 @@
<RootNamespace>VNLib.Plugins.Essentials.Accounts</RootNamespace>
<Copyright>Copyright © 2022 Vaughn Nugent</Copyright>
<Authors>Vaughn Nugent</Authors>
- <AssemblyName>Accounts</AssemblyName>
+ <AssemblyName>Essentials.Accounts</AssemblyName>
<PackageId>VNLib.Plugins.Essentials.Accounts</PackageId>
<Version>1.0.1.5</Version>
@@ -49,7 +49,7 @@
</ItemGroup>
<ItemGroup>
- <None Update="Accounts.json">
+ <None Update="Essentials.Accounts.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/RouteComparer.cs b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/RouteComparer.cs
index 189da62..e214e14 100644
--- a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/RouteComparer.cs
+++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/RouteComparer.cs
@@ -26,7 +26,7 @@ using System.Collections.Generic;
using VNLib.Plugins.Essentials.Content.Routing.Model;
-using static VNLib.Plugins.Essentials.Accounts.AccountManager;
+using static VNLib.Plugins.Essentials.Accounts.AccountUtil;
namespace VNLib.Plugins.Essentials.Content.Routing
{
diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Router.cs b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Router.cs
index 4dc320a..7620809 100644
--- a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Router.cs
+++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Router.cs
@@ -36,7 +36,7 @@ using VNLib.Utils.Logging;
using VNLib.Plugins.Extensions.Loading.Sql;
using VNLib.Plugins.Extensions.Loading.Events;
using VNLib.Plugins.Essentials.Content.Routing.Model;
-using static VNLib.Plugins.Essentials.Accounts.AccountManager;
+using static VNLib.Plugins.Essentials.Accounts.AccountUtil;
namespace VNLib.Plugins.Essentials.Content.Routing
{
diff --git a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientAccessTokenState.cs b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientAccessTokenState.cs
index e5de597..8a7aea3 100644
--- a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientAccessTokenState.cs
+++ b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientAccessTokenState.cs
@@ -69,7 +69,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth
public override int GetHashCode() => Token!.GetHashCode(StringComparison.Ordinal);
void ICacheable.Evicted()
{
- Memory.UnsafeZeroMemory(Nonce);
+ MemoryUtil.UnsafeZeroMemory(Nonce);
}
void INonce.ComputeNonce(Span<byte> buffer)
diff --git a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientRequestState.cs b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientRequestState.cs
index 2f35e48..ba369c2 100644
--- a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientRequestState.cs
+++ b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/ClientRequestState.cs
@@ -74,8 +74,8 @@ namespace VNLib.Plugins.Essentials.SocialOauth
void ICacheable.Evicted()
{
//Zero secrets on eviction
- Memory.UnsafeZeroMemory(State);
- Memory.UnsafeZeroMemory(_rawKey);
+ MemoryUtil.UnsafeZeroMemory(State);
+ MemoryUtil.UnsafeZeroMemory(_rawKey);
}
}
} \ No newline at end of file
diff --git a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs
index 6815bf3..79e3b1b 100644
--- a/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs
+++ b/plugins/VNLib.Plugins.Essentials.SocialOauth/src/SocialOauthBase.cs
@@ -238,7 +238,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth
void ICacheable.Evicted()
{
//Erase nonce
- Memory.UnsafeZeroMemory(RawNonce);
+ MemoryUtil.UnsafeZeroMemory(RawNonce);
}
public override bool Equals(object? obj)
@@ -422,11 +422,11 @@ namespace VNLib.Plugins.Essentials.SocialOauth
//Generate a new random passowrd incase the user wants to use a local account to log in sometime in the future
PrivateString passhash = Config.Passwords.Hash(randomPass);
//overwite the password bytes
- Memory.InitializeBlock(randomPass.AsSpan());
+ MemoryUtil.InitializeBlock(randomPass.AsSpan());
try
{
//Create the user with the specified email address, minimum privilage level, and an empty password
- user = await Config.Users.CreateUserAsync(userLogin.UserId!, userAccount.EmailAddress, AccountManager.MINIMUM_LEVEL, passhash, entity.EventCancellation);
+ user = await Config.Users.CreateUserAsync(userLogin.UserId!, userAccount.EmailAddress, AccountUtil.MINIMUM_LEVEL, passhash, entity.EventCancellation);
//Set active status
user.Status = UserStatus.Active;
//Store the new profile
@@ -570,7 +570,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth
private string BuildUrl(string base32Nonce, string pubKey, ReadOnlySpan<char> scheme, ReadOnlySpan<char> redirectAuthority, Encoding enc)
{
//Char buffer for base32 and url building
- using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(8192, true);
+ using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(8192, true);
//get bin buffer slice
Span<byte> binBuffer = buffer.Span[1024..];
@@ -609,7 +609,7 @@ namespace VNLib.Plugins.Essentials.SocialOauth
//Encode the url to binary
int byteCount = enc.GetBytes(url, encodingBuffer);
//Encrypt the binary
- ERRNO count = AccountManager.TryEncryptClientData(pubKey, encodingBuffer[..byteCount], in encryptionBuffer);
+ ERRNO count = AccountUtil.TryEncryptClientData(pubKey, encodingBuffer[..byteCount], in encryptionBuffer);
//base64 encode the encrypted
return Convert.ToBase64String(encryptionBuffer[0..(int)count]);
}