October3d55/M/LowEntryExtStdLib/Source/LowEntryExtendedStandardLib.../Private/Classes/LowEntryHashingHashcashLibr...

294 lines
7.1 KiB
C++

// Copyright Low Entry. Apache License, Version 2.0.
#include "LowEntryHashingHashcashLibrary.h"
#include "LowEntryExtendedStandardLibrary.h"
const FString ULowEntryHashingHashcashLibrary::DATE_FORMAT_STRING = TEXT("%y%m%d%H%M%S");
TArray<TArray<uint8>> ULowEntryHashingHashcashLibrary::BASE_64_CACHE = TArray<TArray<uint8>>();
TArray<TArray<uint8>> ULowEntryHashingHashcashLibrary::GenerateBase64Cache()
{
TArray<TArray<uint8>> Array;
Array.SetNum(MAX_COUNTER);
for (int32 i = 0; i < Array.Num(); i++)
{
Array[i] = base64WithoutLeadingZeroBytes(i);
}
return Array;
}
TArray<FString> ULowEntryHashingHashcashLibrary::hashArray(const TArray<FString>& resource, const int32 bits)
{
return hashArrayCustomCreationDate(resource, FDateTime::UtcNow(), bits);
}
TArray<FString> ULowEntryHashingHashcashLibrary::hashArrayCustomCreationDate(const TArray<FString>& resources, const FDateTime& date, const int32 bits)
{
TArray<FString> result;
result.SetNum(resources.Num());
for (int64 i = 0; i < resources.Num(); i++)
{
result[i] = hashCustomCreationDate(resources[i], date, bits);
}
return result;
}
FString ULowEntryHashingHashcashLibrary::hash(const FString& resource, const int32 bits)
{
return hashCustomCreationDate(resource, FDateTime::UtcNow(), bits);
}
FString ULowEntryHashingHashcashLibrary::hashCustomCreationDate(const FString& resource, const FDateTime& date, const int32 bits)
{
FSHA1 hasher;
TArray<uint8> hashBuffer;
hashBuffer.SetNum(FSHA1::DigestSize);
FString dataPrefix = FString::FromInt(VERSION) + TEXT(":") + FString::FromInt(bits) + TEXT(":") + getDateString(date) + TEXT(":") + resource + TEXT("::");
TArray<uint8> dataPrefixBytes = ULowEntryExtendedStandardLibrary::StringToBytesUtf8(dataPrefix);
TArray<uint8> buffer;
buffer.Reserve(dataPrefixBytes.Num() + 17 + 8);
buffer.Append(dataPrefixBytes);
int32 prefixpos = buffer.Num();
while (true)
{
buffer.SetNum(prefixpos, EAllowShrinking::No);
TArray<uint8> randomBytes;
ULowEntryExtendedStandardLibrary::GenerateRandomBytes(12, randomBytes);
buffer.Append(ULowEntryExtendedStandardLibrary::StringToBytesUtf8(ULowEntryExtendedStandardLibrary::BytesToBase64(randomBytes) + TEXT(":")));
if (BASE_64_CACHE.Num() <= 0)
{
BASE_64_CACHE = GenerateBase64Cache();
}
for (const TArray<uint8>& base64counter : BASE_64_CACHE)
{
hasher.Reset();
hasher.Update(buffer.GetData(), buffer.Num());
hasher.Update(base64counter.GetData(), base64counter.Num());
hasher.Final();
hasher.GetHash(hashBuffer.GetData());
int32 leadingZeroBits = countLeadingZeroBits(hashBuffer);
if (leadingZeroBits >= bits)
{
buffer.Append(base64counter);
return ULowEntryExtendedStandardLibrary::BytesToStringUtf8(buffer);
}
}
}
}
TArray<ULowEntryParsedHashcash*> ULowEntryHashingHashcashLibrary::parseArray(const TArray<FString>& Hashcashes)
{
TArray<ULowEntryParsedHashcash*> result;
result.SetNum(Hashcashes.Num());
for (int64 i = 0; i < Hashcashes.Num(); i++)
{
result[i] = parse(Hashcashes[i]);
}
return result;
}
ULowEntryParsedHashcash* ULowEntryHashingHashcashLibrary::parse(const FString& Hashcash)
{
TArray<FString> parts;
Hashcash.ParseIntoArray(parts, TEXT(":"), false);
if (parts.Num() >= 6)
{
FString version = parts[0];
if (version.Equals(TEXT("0")))
{
FDateTime date = parseDateString(parts[1]);
if (date.GetYear() > 1)
{
int32 bits = countLeadingZeroBits(ULowEntryExtendedStandardLibrary::Sha1(ULowEntryExtendedStandardLibrary::StringToBytesUtf8(Hashcash)));
FString resource = parts[2];
for (int32 i = 3; i < (parts.Num() - 3); i++)
{
resource += TEXT(":") + parts[i];
}
return ULowEntryParsedHashcash::Create(true, resource, date, bits);
}
}
else if (version.Equals(TEXT("1")))
{
if (parts.Num() >= 7)
{
FDateTime date = parseDateString(parts[2]);
if (date.GetYear() > 1)
{
int32 bits = FCString::Atoi(*parts[1]);
if (countLeadingZeroBits(ULowEntryExtendedStandardLibrary::Sha1(ULowEntryExtendedStandardLibrary::StringToBytesUtf8(Hashcash))) >= bits)
{
FString resource = parts[3];
for (int32 i = 4; i < (parts.Num() - 3); i++)
{
resource += TEXT(":") + parts[i];
}
return ULowEntryParsedHashcash::Create(true, resource, date, bits);
}
}
}
}
}
return ULowEntryParsedHashcash::Create(false, TEXT(""), FDateTime(0), 0);
}
TArray<uint8> ULowEntryHashingHashcashLibrary::base64WithoutLeadingZeroBytes(const int32 value)
{
TArray<uint8> bytes;
if (value < 0)
{
bytes.SetNum(4);
bytes[0] = static_cast<uint8>(value >> 24);
bytes[1] = static_cast<uint8>(value >> 16);
bytes[2] = static_cast<uint8>(value >> 8);
bytes[3] = static_cast<uint8>(value);
}
else if (value < 256)
{
bytes.SetNum(1);
bytes[0] = static_cast<uint8>(value);
}
else if (value < 65536)
{
bytes.SetNum(2);
bytes[0] = static_cast<uint8>(value >> 8);
bytes[1] = static_cast<uint8>(value);
}
else if (value < 16777216)
{
bytes.SetNum(3);
bytes[0] = static_cast<uint8>(value >> 16);
bytes[1] = static_cast<uint8>(value >> 8);
bytes[2] = static_cast<uint8>(value);
}
else
{
bytes.SetNum(4);
bytes[0] = static_cast<uint8>(value >> 24);
bytes[1] = static_cast<uint8>(value >> 16);
bytes[2] = static_cast<uint8>(value >> 8);
bytes[3] = static_cast<uint8>(value);
}
return ULowEntryExtendedStandardLibrary::StringToBytesUtf8(ULowEntryExtendedStandardLibrary::BytesToBase64(bytes));
}
FString ULowEntryHashingHashcashLibrary::getDateString(const FDateTime& date)
{
return date.ToString(*DATE_FORMAT_STRING);
}
FDateTime ULowEntryHashingHashcashLibrary::parseDateString(const FString& date)
{
if (date.Len() < 6)
{
return FDateTime(0);
}
int32 year = FCString::Atoi(*date.Mid(0, 2));
int32 month = FCString::Atoi(*date.Mid(2, 2));
int32 day = FCString::Atoi(*date.Mid(4, 2));
if ((year < 0) || (month < 1) || (month > 12))
{
return FDateTime(0);
}
year += ((FDateTime::Now().GetYear() / 100) * 100);
if ((day < 1) || (day > FDateTime::DaysInMonth(year, month)))
{
return FDateTime(0);
}
if (date.Len() < 10)
{
return FDateTime(year, month, day);
}
int32 hour = FCString::Atoi(*date.Mid(6, 2));
int32 minutes = FCString::Atoi(*date.Mid(8, 2));
if ((hour < 0) || (hour > 23) || (minutes < 0) || (minutes > 59))
{
return FDateTime(0);
}
if (date.Len() < 12)
{
return FDateTime(year, month, day, hour, minutes);
}
int32 seconds = FCString::Atoi(*date.Mid(10, 2));
if ((seconds < 0) || (seconds > 59))
{
return FDateTime(0);
}
return FDateTime(year, month, day, hour, minutes, seconds);
}
int32 ULowEntryHashingHashcashLibrary::countLeadingZeroBits(const TArray<uint8>& values)
{
int32 total = 0;
for (uint8 value : values)
{
uint8 ofbyte = countLeadingZeroBits(value);
total += ofbyte;
if (ofbyte != 8)
{
break;
}
}
return total;
}
uint8 ULowEntryHashingHashcashLibrary::countLeadingZeroBits(const uint8 v)
{
if (v < 0)
{
return 0;
}
if (v < 1)
{
return 8;
}
if (v < 2)
{
return 7;
}
if (v < 4)
{
return 6;
}
if (v < 8)
{
return 5;
}
if (v < 16)
{
return 4;
}
if (v < 32)
{
return 3;
}
if (v < 64)
{
return 2;
}
if (v < 128)
{
return 1;
}
return 0;
}