mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-29 04:02:28 +00:00
Multiple changes to HashHelper and RandomHelper
- Renamed HashHelper to CryptoHelper - Moved CryptoHelper.GenerateRandomBytes to RandomHelper - Documented RandomHelper
This commit is contained in:
parent
396477c05d
commit
f31a73ccaa
22 changed files with 86 additions and 86 deletions
|
@ -16,8 +16,8 @@ public class DatabaseTests : LighthouseServerTest
|
|||
await using Database database = new();
|
||||
int rand = new Random().Next();
|
||||
|
||||
User userA = await database.CreateUser("unitTestUser" + rand, HashHelper.GenerateAuthToken());
|
||||
User userB = await database.CreateUser("unitTestUser" + rand, HashHelper.GenerateAuthToken());
|
||||
User userA = await database.CreateUser("unitTestUser" + rand, CryptoHelper.GenerateAuthToken());
|
||||
User userB = await database.CreateUser("unitTestUser" + rand, CryptoHelper.GenerateAuthToken());
|
||||
|
||||
Assert.NotNull(userA);
|
||||
Assert.NotNull(userB);
|
||||
|
|
|
@ -20,8 +20,8 @@ public class SlotTests : LighthouseServerTest
|
|||
|
||||
Random r = new();
|
||||
|
||||
User userA = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken());
|
||||
User userB = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken());
|
||||
User userA = await database.CreateUser($"unitTestUser{r.Next()}", CryptoHelper.GenerateAuthToken());
|
||||
User userB = await database.CreateUser($"unitTestUser{r.Next()}", CryptoHelper.GenerateAuthToken());
|
||||
|
||||
Location l = new()
|
||||
{
|
||||
|
|
|
@ -18,12 +18,12 @@ public class AdminTests : LighthouseWebTest
|
|||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
database.WebTokens.Add(webToken);
|
||||
|
@ -42,12 +42,12 @@ public class AdminTests : LighthouseWebTest
|
|||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
database.WebTokens.Add(webToken);
|
||||
|
|
|
@ -19,8 +19,8 @@ public class AuthenticationTests : LighthouseWebTest
|
|||
await using Database database = new();
|
||||
Random random = new();
|
||||
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password)));
|
||||
string password = CryptoHelper.Sha256Hash(RandomHelper.GenerateRandomBytes(64).ToArray());
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash(CryptoHelper.Sha256Hash(password)));
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||
|
||||
|
@ -40,7 +40,7 @@ public class AuthenticationTests : LighthouseWebTest
|
|||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,"));
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("just like the hindenberg,"));
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class AuthenticationTests : LighthouseWebTest
|
|||
{
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||
|
||||
|
@ -81,12 +81,12 @@ public class AuthenticationTests : LighthouseWebTest
|
|||
|
||||
await using Database database = new();
|
||||
Random random = new();
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", CryptoHelper.BCryptHash("i'm an engineering failure"));
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
database.WebTokens.Add(webToken);
|
||||
|
|
|
@ -19,7 +19,7 @@ public class RegisterTests : LighthouseWebTest
|
|||
await using Database database = new();
|
||||
|
||||
string username = ("unitTestUser" + new Random().Next()).Substring(0, 16);
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
string password = CryptoHelper.Sha256Hash(RandomHelper.GenerateRandomBytes(64).ToArray());
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||
|
||||
|
@ -42,7 +42,7 @@ public class RegisterTests : LighthouseWebTest
|
|||
await using Database database = new();
|
||||
|
||||
string username = ("unitTestUser" + new Random().Next()).Substring(0, 16);
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
string password = CryptoHelper.Sha256Hash(RandomHelper.GenerateRandomBytes(64).ToArray());
|
||||
|
||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||
|
||||
|
@ -63,9 +63,9 @@ public class RegisterTests : LighthouseWebTest
|
|||
await using Database database = new();
|
||||
|
||||
string username = ("unitTestUser" + new Random().Next()).Substring(0, 16);
|
||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||
string password = CryptoHelper.Sha256Hash(RandomHelper.GenerateRandomBytes(64).ToArray());
|
||||
|
||||
await database.CreateUser(username, HashHelper.BCryptHash(password));
|
||||
await database.CreateUser(username, CryptoHelper.BCryptHash(password));
|
||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
Assert.NotNull(user);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ public class LighthouseServerTest
|
|||
{
|
||||
await using Database database = new();
|
||||
if (await database.Users.FirstOrDefaultAsync(u => u.Username == $"{username}{number}") == null)
|
||||
await database.CreateUser($"{username}{number}", HashHelper.BCryptHash($"unitTestPassword{number}"));
|
||||
await database.CreateUser($"{username}{number}", CryptoHelper.BCryptHash($"unitTestPassword{number}"));
|
||||
}
|
||||
|
||||
//TODO: generate actual tickets
|
||||
|
@ -68,7 +68,7 @@ public class LighthouseServerTest
|
|||
public async Task<HttpResponseMessage> UploadFileEndpointRequest(string filePath)
|
||||
{
|
||||
byte[] bytes = await File.ReadAllBytesAsync(filePath);
|
||||
string hash = HashHelper.Sha1Hash(bytes).ToLower();
|
||||
string hash = CryptoHelper.Sha1Hash(bytes).ToLower();
|
||||
|
||||
return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes));
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public class LighthouseServerTest
|
|||
public async Task<HttpResponseMessage> AuthenticatedUploadFileEndpointRequest(string filePath, string mmAuth)
|
||||
{
|
||||
byte[] bytes = await File.ReadAllBytesAsync(filePath);
|
||||
string hash = HashHelper.Sha1Hash(bytes).ToLower();
|
||||
string hash = CryptoHelper.Sha1Hash(bytes).ToLower();
|
||||
using HttpRequestMessage requestMessage = new(HttpMethod.Post, $"/LITTLEBIGPLANETPS3_XML/upload/{hash}");
|
||||
requestMessage.Headers.Add("Cookie", mmAuth);
|
||||
requestMessage.Content = new ByteArrayContent(bytes);
|
||||
|
|
|
@ -17,6 +17,20 @@ public class MessageController : ControllerBase
|
|||
{
|
||||
private readonly Database database;
|
||||
|
||||
private const string license = @"
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
||||
|
||||
public MessageController(Database database)
|
||||
{
|
||||
this.database = database;
|
||||
|
@ -28,7 +42,7 @@ public class MessageController : ControllerBase
|
|||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}");
|
||||
return this.Ok($"{license}\n{ServerSettings.Instance.EulaText}");
|
||||
}
|
||||
|
||||
[HttpGet("announce")]
|
||||
|
|
|
@ -100,7 +100,7 @@ public class Database : DbContext
|
|||
|
||||
GameToken gameToken = new()
|
||||
{
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
User = user,
|
||||
UserId = user.UserId,
|
||||
UserLocation = userLocation,
|
||||
|
|
|
@ -48,7 +48,7 @@ public static class CensorHelper
|
|||
{
|
||||
case FilterMode.Random:
|
||||
for(int i = 0; i < profanityLength; i++)
|
||||
lock(RandomHelper.random)
|
||||
lock(RandomHelper.Random)
|
||||
{
|
||||
if (message[i] == ' ')
|
||||
{
|
||||
|
@ -56,8 +56,8 @@ public static class CensorHelper
|
|||
}
|
||||
else
|
||||
{
|
||||
char randomChar = randomCharacters[RandomHelper.random.Next(0, randomCharacters.Length - 1)];
|
||||
if (randomChar == prevRandomChar) randomChar = randomCharacters[RandomHelper.random.Next(0, randomCharacters.Length - 1)];
|
||||
char randomChar = randomCharacters[RandomHelper.Random.Next(0, randomCharacters.Length - 1)];
|
||||
if (randomChar == prevRandomChar) randomChar = randomCharacters[RandomHelper.Random.Next(0, randomCharacters.Length - 1)];
|
||||
|
||||
prevRandomChar = randomChar;
|
||||
|
||||
|
@ -81,9 +81,9 @@ public static class CensorHelper
|
|||
|
||||
break;
|
||||
case FilterMode.Furry:
|
||||
lock(RandomHelper.random)
|
||||
lock(RandomHelper.Random)
|
||||
{
|
||||
string randomWord = randomFurry[RandomHelper.random.Next(0, randomFurry.Length - 1)];
|
||||
string randomWord = randomFurry[RandomHelper.Random.Next(0, randomFurry.Length - 1)];
|
||||
sb.Append(randomWord);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
@ -9,35 +8,18 @@ using System.Threading.Tasks;
|
|||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static class HashHelper
|
||||
public static class CryptoHelper
|
||||
{
|
||||
// private static readonly SHA1 sha1 = SHA1.Create();
|
||||
private static readonly SHA256 sha256 = SHA256.Create();
|
||||
|
||||
/// <summary>
|
||||
/// Generates a specified amount of random bytes in an array.
|
||||
/// </summary>
|
||||
/// <param name="count">The amount of bytes to generate.</param>
|
||||
/// <returns>The bytes generated</returns>
|
||||
public static IEnumerable<byte> GenerateRandomBytes(int count)
|
||||
{
|
||||
byte[] b = new byte[count];
|
||||
|
||||
lock(RandomHelper.random)
|
||||
{
|
||||
RandomHelper.random.NextBytes(b);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random SHA256 and BCrypted token
|
||||
/// </summary>
|
||||
/// <returns>The token as a string.</returns>
|
||||
public static string GenerateAuthToken()
|
||||
{
|
||||
byte[] bytes = (byte[])GenerateRandomBytes(256);
|
||||
byte[] bytes = (byte[])RandomHelper.GenerateRandomBytes(256);
|
||||
|
||||
return BCryptHash(Sha256Hash(bytes));
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
public static class EulaHelper
|
||||
{
|
||||
public const string License = @"
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
||||
}
|
|
@ -1,8 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
public static class RandomHelper
|
||||
{
|
||||
public static readonly Random random = new();
|
||||
/// <summary>
|
||||
/// An instance of Random. Must be locked when in use.
|
||||
/// </summary>
|
||||
public static readonly Random Random = new();
|
||||
|
||||
/// <summary>
|
||||
/// Generates a specified amount of random bytes in an array.
|
||||
/// </summary>
|
||||
/// <param name="count">The amount of bytes to generate.</param>
|
||||
/// <returns>The bytes generated</returns>
|
||||
public static IEnumerable<byte> GenerateRandomBytes(int count)
|
||||
{
|
||||
byte[] b = new byte[count];
|
||||
|
||||
lock(Random)
|
||||
{
|
||||
Random.NextBytes(b);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
|
@ -19,12 +19,12 @@ public class CreateUserCommand : ICommand
|
|||
string onlineId = args[0];
|
||||
string password = args[1];
|
||||
|
||||
password = HashHelper.Sha256Hash(password);
|
||||
password = CryptoHelper.Sha256Hash(password);
|
||||
|
||||
User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId);
|
||||
if (user == null)
|
||||
{
|
||||
user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password));
|
||||
user = await this._database.CreateUser(onlineId, CryptoHelper.BCryptHash(password));
|
||||
Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance);
|
||||
|
||||
user.PasswordResetRequired = true;
|
||||
|
|
|
@ -36,9 +36,9 @@ public class ResetPasswordCommand : ICommand
|
|||
return;
|
||||
}
|
||||
string password = args[1];
|
||||
if (password.Length != 64) password = HashHelper.Sha256Hash(password);
|
||||
if (password.Length != 64) password = CryptoHelper.Sha256Hash(password);
|
||||
|
||||
user.Password = HashHelper.BCryptHash(password);
|
||||
user.Password = CryptoHelper.BCryptHash(password);
|
||||
user.PasswordResetRequired = true;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
|
|
@ -74,7 +74,7 @@ public class LoginForm : BaseLayout
|
|||
{
|
||||
UserId = user.UserId,
|
||||
User = user,
|
||||
EmailToken = HashHelper.GenerateAuthToken(),
|
||||
EmailToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
this.Database.EmailSetTokens.Add(emailSetToken);
|
||||
|
@ -86,7 +86,7 @@ public class LoginForm : BaseLayout
|
|||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
this.Database.WebTokens.Add(webToken);
|
||||
|
|
|
@ -33,7 +33,7 @@ public class PasswordResetPage : BaseLayout
|
|||
return this.Page();
|
||||
}
|
||||
|
||||
user.Password = HashHelper.BCryptHash(password);
|
||||
user.Password = CryptoHelper.BCryptHash(password);
|
||||
user.PasswordResetRequired = false;
|
||||
|
||||
await this.Database.SaveChangesAsync();
|
||||
|
|
|
@ -67,12 +67,12 @@ public class RegisterForm : BaseLayout
|
|||
return this.Page();
|
||||
}
|
||||
|
||||
User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password), emailAddress);
|
||||
User user = await this.Database.CreateUser(username, CryptoHelper.BCryptHash(password), emailAddress);
|
||||
|
||||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
this.Database.WebTokens.Add(webToken);
|
||||
|
|
|
@ -38,7 +38,7 @@ public class SendVerificationEmailPage : BaseLayout
|
|||
{
|
||||
UserId = user.UserId,
|
||||
User = user,
|
||||
EmailToken = HashHelper.GenerateAuthToken(),
|
||||
EmailToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
this.Database.EmailVerificationTokens.Add(verifyToken);
|
||||
|
|
|
@ -50,7 +50,7 @@ public class SetEmailForm : BaseLayout
|
|||
{
|
||||
UserId = user.UserId,
|
||||
User = user,
|
||||
EmailToken = HashHelper.GenerateAuthToken(),
|
||||
EmailToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
this.Database.EmailVerificationTokens.Add(emailVerifyToken);
|
||||
|
@ -59,7 +59,7 @@ public class SetEmailForm : BaseLayout
|
|||
WebToken webToken = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
UserToken = HashHelper.GenerateAuthToken(),
|
||||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
};
|
||||
|
||||
this.Response.Cookies.Append
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Migrations\20220301204930_AddEmailVerificationTokens.Designer.cs" />
|
||||
<Compile Remove="Helpers\EulaHelper.cs"/>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
|
|
|
@ -173,7 +173,7 @@ public class Startup
|
|||
|
||||
if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML"))
|
||||
{
|
||||
string clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.ServerDigestKey);
|
||||
string clientRequestDigest = await CryptoHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.ServerDigestKey);
|
||||
|
||||
// Check the digest we've just calculated against the X-Digest-A header if the game set the header. They should match.
|
||||
if (context.Request.Headers.TryGetValue("X-Digest-A", out StringValues sentDigest))
|
||||
|
@ -186,7 +186,7 @@ public class Startup
|
|||
// Reset the body stream
|
||||
body.Position = 0;
|
||||
|
||||
clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.AlternateDigestKey);
|
||||
clientRequestDigest = await CryptoHelper.ComputeDigest(digestPath, authCookie, body, ServerSettings.Instance.AlternateDigestKey);
|
||||
if (clientRequestDigest != sentDigest)
|
||||
{
|
||||
#if DEBUG
|
||||
|
@ -222,7 +222,7 @@ public class Startup
|
|||
string digestKey = usedAlternateDigestKey ? ServerSettings.Instance.AlternateDigestKey : ServerSettings.Instance.ServerDigestKey;
|
||||
|
||||
// Compute the digest for the response.
|
||||
string serverDigest = await HashHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey);
|
||||
string serverDigest = await CryptoHelper.ComputeDigest(context.Request.Path, authCookie, responseBuffer, digestKey);
|
||||
context.Response.Headers.Add("X-Digest-A", serverDigest);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ public class LbpFile
|
|||
this.Data = data;
|
||||
|
||||
this.FileType = FileHelper.DetermineFileType(this.Data);
|
||||
this.Hash = HashHelper.Sha1Hash(this.Data).ToLower();
|
||||
this.Hash = CryptoHelper.Sha1Hash(this.Data).ToLower();
|
||||
}
|
||||
|
||||
public static LbpFile? FromHash(string hash)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue