diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d94c5a1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/secp256k1"] + path = Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/secp256k1 + url = https://github.com/bitcoin-core/secp256k1 diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/ECCrypto.cpp b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/ECCrypto.cpp new file mode 100644 index 0000000..c77866b --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/ECCrypto.cpp @@ -0,0 +1,230 @@ +#include "ECCrypto.h" +#include +#include +#include + +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "WS2_32.lib") + + +unsigned char* toByteArray(const std::string& s) { + size_t len = s.length() / 2; + unsigned char* data = new unsigned char[len]; + + for (size_t i = 0; i < len * 2; i += 2) { + int hi = std::stoi(s.substr(i, 1), nullptr, 16); + int lo = std::stoi(s.substr(i + 1, 1), nullptr, 16); + data[i / 2] = (unsigned char)((hi << 4) + lo); + } + + return data; + +} + +char* FStringToCharArray(const FString& InString) { + char* CharArray = new char[InString.Len() + 1]; + + strcpy(CharArray, TCHAR_TO_ANSI(*InString)); + return CharArray; +} + +UECCrypto::UECCrypto() { +} + +FString UECCrypto::decrypt(FString data, FString privateKeyHex, FString ephemPublicKeyHex, FString encryptionIvHex) +{ + // Convert to bytes array + const char* priv_hex = FStringToCharArray(privateKeyHex); + const char* pub_hex = FStringToCharArray(ephemPublicKeyHex); + + // Decode IV key + const unsigned char* iv = toByteArray(FStringToCharArray(encryptionIvHex)); + + // Decode cipher text + const unsigned char* src = toByteArray(FStringToCharArray(data)); + int srclen = data.Len() / 2; + + // Convert to BIGNUM + BIGNUM* priv_bn = BN_new(); + BIGNUM* pub_bn = BN_new(); + BN_hex2bn(&priv_bn, priv_hex); + BN_hex2bn(&pub_bn, pub_hex); + + // Create EC_KEY objects from the BIGNUMs + EC_KEY* priv_key = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY* pub_key = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY_set_private_key(priv_key, priv_bn); + EC_KEY_set_public_key(pub_key, EC_POINT_bn2point(EC_KEY_get0_group(pub_key), pub_bn, NULL, NULL)); + + // Create the shared secret + unsigned char* secret = new unsigned char[32]; + int secret_len = ECDH_compute_key(secret, EVP_MAX_KEY_LENGTH, EC_KEY_get0_public_key(pub_key), priv_key, NULL); + + // Calculate SHA-512 hash of secret + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512(secret, 32, hash); + + // Copy first 32 bytes of the hash into a new buffer + unsigned char key[32]; + memcpy(key, hash, 32); + + // Create a new encryption context for AES-256 CBC mode with the key and IV + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); + + // Allocate a string buffer for the decrypted data + std::string dst; + dst.resize(srclen + EVP_CIPHER_block_size(EVP_aes_256_cbc())); + + // Decrypt the input data + int outlen; + EVP_DecryptUpdate(ctx, (unsigned char*)dst.data(), &outlen, src, srclen); + + // Finalize the decryption and retrieve any remaining data + int finaloutlen; + EVP_DecryptFinal_ex(ctx, (unsigned char*)dst.data() + outlen, &finaloutlen); + + // Resize the buffer to the actual decrypted length + dst.resize(outlen + finaloutlen); + + // Free the encryption context + EVP_CIPHER_CTX_free(ctx); + + // Clean up resources + BN_free(priv_bn); + BN_free(pub_bn); + EC_KEY_free(priv_key); + EC_KEY_free(pub_key); + EVP_cleanup(); + + return FString(dst.c_str()); +} + +FString UECCrypto::encrypt(FString data, FString privateKeyHex, FString ephemPublicKeyHex, FString encryptionIvHex) +{ + // Convert to bytes array + const char* priv_hex = FStringToCharArray(privateKeyHex); + const char* pub_hex = FStringToCharArray(ephemPublicKeyHex); + + // Decode IV key + const unsigned char* iv = toByteArray(FStringToCharArray(encryptionIvHex)); + + // Decode cipher text + const unsigned char* src = (unsigned char*)FStringToCharArray(data); + int srclen = data.Len() / 2; + + // Convert to BIGNUM + BIGNUM* priv_bn = BN_new(); + BIGNUM* pub_bn = BN_new(); + BN_hex2bn(&priv_bn, priv_hex); + BN_hex2bn(&pub_bn, pub_hex); + + // Create EC_KEY objects from the BIGNUMs + EC_KEY* priv_key = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY* pub_key = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY_set_private_key(priv_key, priv_bn); + EC_KEY_set_public_key(pub_key, EC_POINT_bn2point(EC_KEY_get0_group(pub_key), pub_bn, NULL, NULL)); + + // Create the shared secret + unsigned char* secret = new unsigned char[32]; + int secret_len = ECDH_compute_key(secret, EVP_MAX_KEY_LENGTH, EC_KEY_get0_public_key(pub_key), priv_key, NULL); + + // Calculate SHA-512 hash of secret + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512(secret, 32, hash); + + // Copy first 32 bytes of the hash into a new buffer + unsigned char key[32]; + memcpy(key, hash, 32); + + // Create a new encryption context for AES-256 CBC mode with the key and IV + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); + + // Allocate a string buffer for the decrypted data + std::string dst; + dst.resize(srclen + EVP_CIPHER_block_size(EVP_aes_256_cbc())); + + // Decrypt the input data + int outlen; + EVP_EncryptUpdate(ctx, (unsigned char*)dst.data(), &outlen, src, srclen); + + // Finalize the decryption and retrieve any remaining data + int finaloutlen; + EVP_EncryptFinal_ex(ctx, (unsigned char*)dst.data() + outlen, &finaloutlen); + + // Resize the buffer to the actual decrypted length + dst.resize(outlen + finaloutlen); + + // Free the encryption context + EVP_CIPHER_CTX_free(ctx); + + // Clean up resources + BN_free(priv_bn); + BN_free(pub_bn); + EC_KEY_free(priv_key); + EC_KEY_free(pub_key); + EVP_cleanup(); + + return FString(UTF8_TO_TCHAR(dst.c_str())); +} + +FString UECCrypto::generatePublicKey(const FString& privateKeyHex) { + BIGNUM* bn_private_key = NULL; + BN_hex2bn(&bn_private_key, TCHAR_TO_ANSI(*privateKeyHex)); + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY_set_private_key(ec_key, bn_private_key); + + EC_POINT* ec_point = EC_POINT_new(EC_KEY_get0_group(ec_key)); + EC_POINT_mul(EC_KEY_get0_group(ec_key), ec_point, EC_KEY_get0_private_key(ec_key), NULL, NULL, NULL); + EC_KEY_set_public_key(ec_key, ec_point); + + BIGNUM* bn = EC_POINT_point2bn(EC_KEY_get0_group(ec_key), EC_KEY_get0_public_key(ec_key), POINT_CONVERSION_UNCOMPRESSED, NULL, NULL); + + char* hex = BN_bn2hex(bn); + FString result(UTF8_TO_TCHAR(hex)); + + OPENSSL_free(hex); + BN_free(bn_private_key); + + return result.ToLower(); +} + +FString UECCrypto::generateECDSASignature(const FString& privateKeyHex, const FString& data) { + // Initialize OpenSSL's elliptic curve library + EC_KEY* key = EC_KEY_new_by_curve_name(NID_secp256k1); + + BIGNUM* priv_bn = BN_new(); + BN_hex2bn(&priv_bn, FStringToCharArray(privateKeyHex)); + + EC_KEY_set_private_key(key, priv_bn); + + const unsigned char* msg = (const unsigned char* ) FStringToCharArray(data); + size_t msglen = data.Len(); + + unsigned char hash[SHA256_DIGEST_LENGTH]; + Keccak256::getHash(msg, msglen, hash); + + unsigned char* sig_buf = nullptr; + + ECDSA_SIG* signature = ECDSA_do_sign(hash, SHA256_DIGEST_LENGTH, key); + int n = i2d_ECDSA_SIG(signature, &sig_buf); + + //// Convert signature to hex string + FString signature_hex; + for (int i = 0; i < n; ++i) { + signature_hex += FString::Printf(TEXT("%02x"), sig_buf[i]); + } + + EC_KEY_free(key); + ECDSA_SIG_free(signature); + + return signature_hex; +} + +UECCrypto::~UECCrypto() +{ +} + + diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Keccak256.cpp b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Keccak256.cpp new file mode 100644 index 0000000..1292c69 --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Keccak256.cpp @@ -0,0 +1,101 @@ +/* + * Bitcoin cryptography library + * Copyright (c) Project Nayuki + * + * https://www.nayuki.io/page/bitcoin-cryptography-library + * https://github.com/nayuki/Bitcoin-Cryptography-Library + */ + +#include "Keccak256.h" +#include + +using std::uint8_t; +using std::uint64_t; +using std::size_t; + + +void Keccak256::getHash(const uint8_t msg[], size_t len, uint8_t hashResult[HASH_LEN]) { + assert((msg != nullptr || len == 0) && hashResult != nullptr); + uint64_t state[5][5] = {}; + + // XOR each message byte into the state, and absorb full blocks + int blockOff = 0; + for (size_t i = 0; i < len; i++) { + int j = blockOff >> 3; + state[j % 5][j / 5] ^= static_cast(msg[i]) << ((blockOff & 7) << 3); + blockOff++; + if (blockOff == BLOCK_SIZE) { + absorb(state); + blockOff = 0; + } + } + + // Final block and padding + { + int i = blockOff >> 3; + state[i % 5][i / 5] ^= UINT64_C(0x01) << ((blockOff & 7) << 3); + blockOff = BLOCK_SIZE - 1; + int j = blockOff >> 3; + state[j % 5][j / 5] ^= UINT64_C(0x80) << ((blockOff & 7) << 3); + absorb(state); + } + + // Uint64 array to bytes in little endian + for (int i = 0; i < HASH_LEN; i++) { + int j = i >> 3; + hashResult[i] = static_cast(state[j % 5][j / 5] >> ((i & 7) << 3)); + } +} + + +void Keccak256::absorb(uint64_t state[5][5]) { + uint64_t(*a)[5] = state; + uint8_t r = 1; // LFSR + for (int i = 0; i < NUM_ROUNDS; i++) { + // Theta step + uint64_t c[5] = {}; + for (int x = 0; x < 5; x++) { + for (int y = 0; y < 5; y++) + c[x] ^= a[x][y]; + } + for (int x = 0; x < 5; x++) { + uint64_t d = c[(x + 4) % 5] ^ rotl64(c[(x + 1) % 5], 1); + for (int y = 0; y < 5; y++) + a[x][y] ^= d; + } + + // Rho and pi steps + uint64_t b[5][5]; + for (int x = 0; x < 5; x++) { + for (int y = 0; y < 5; y++) + b[y][(x * 2 + y * 3) % 5] = rotl64(a[x][y], ROTATION[x][y]); + } + + // Chi step + for (int x = 0; x < 5; x++) { + for (int y = 0; y < 5; y++) + a[x][y] = b[x][y] ^ (~b[(x + 1) % 5][y] & b[(x + 2) % 5][y]); + } + + // Iota step + for (int j = 0; j < 7; j++) { + a[0][0] ^= static_cast(r & 1) << ((1 << j) - 1); + r = static_cast((r << 1) ^ ((r >> 7) * 0x171)); + } + } +} + + +uint64_t Keccak256::rotl64(uint64_t x, int i) { + return ((0U + x) << i) | (x >> ((64 - i) & 63)); +} + + +// Static initializers +const unsigned char Keccak256::ROTATION[5][5] = { + { 0, 36, 3, 41, 18}, + { 1, 44, 10, 45, 2}, + {62, 6, 43, 15, 61}, + {28, 55, 25, 21, 56}, + {27, 20, 39, 8, 14}, +}; \ No newline at end of file diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/KeyStoreUtils.cpp b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/KeyStoreUtils.cpp new file mode 100644 index 0000000..b0e3a32 --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/KeyStoreUtils.cpp @@ -0,0 +1,30 @@ +#include "KeyStoreUtils.h" + +UKeyStoreUtils::UKeyStoreUtils() { + StorageInstance = Cast(UGameplayStatics::LoadGameFromSlot(TEXT("Web3AuthDataSlot"), 0)); + if(StorageInstance == nullptr) { + StorageInstance = Cast(UGameplayStatics::CreateSaveGameObject(UWeb3StorageAdapter::StaticClass())); + } +} + +UKeyStoreUtils::~UKeyStoreUtils() { +} + +void UKeyStoreUtils::Add(FString key, FString value) { + StorageInstance->KeyValuePairs.Add(key, value); + UGameplayStatics::SaveGameToSlot(StorageInstance, TEXT("Web3AuthDataSlot"), 0); +} + +FString UKeyStoreUtils::Get(FString key) { + if (StorageInstance->KeyValuePairs.Contains(key)) { + return StorageInstance->KeyValuePairs[key]; + } + return ""; +} + +void UKeyStoreUtils::Remove(FString key) { + if (StorageInstance->KeyValuePairs.Contains(key)) { + StorageInstance->KeyValuePairs.Remove(key); + UGameplayStatics::SaveGameToSlot(StorageInstance, TEXT("Web3AuthDataSlot"), 0); + } +} diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp index eb86d7e..e0db502 100644 --- a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp @@ -1,4 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. +// Fill out your copyright notice in the Description page of Project Settings. #include "Web3Auth.h" @@ -10,6 +10,9 @@ FOnLogin AWeb3Auth::loginEvent; FOnLogout AWeb3Auth::logoutEvent; +UKeyStoreUtils* AWeb3Auth::keyStoreUtils; +UECCrypto* AWeb3Auth::crypto; + #if PLATFORM_ANDROID JNI_METHOD void Java_com_epicgames_unreal_GameActivity_onDeepLink(JNIEnv* env, jclass clazz, jstring uri) { if (JNIEnv* Env = FAndroidApplication::GetJavaEnv(true)) { @@ -44,6 +47,10 @@ AWeb3Auth::AWeb3Auth() void AWeb3Auth::setOptions(FWeb3AuthOptions options) { this->web3AuthOptions = options; + this->keyStoreUtils = NewObject(); + this->crypto = NewObject(); + + authorizeSession(); } void AWeb3Auth::request(FString path, FLoginParams* loginParams = NULL, TSharedPtr extraParams = NULL) { @@ -161,7 +168,8 @@ void AWeb3Auth::proccessLogout(FString redirectUrl, FString appState) { if (appState != "") extraParams->SetStringField("appState", appState); - this->request("logout", NULL, extraParams); + //this->request("logout", NULL, extraParams); + sessionTimeout(); } void AWeb3Auth::setResultUrl(FString hash) { @@ -207,6 +215,7 @@ void AWeb3Auth::setResultUrl(FString hash) { }); } else { + AWeb3Auth::keyStoreUtils->Add("sessionid", web3AuthResponse.sessionId); AsyncTask(ENamedThreads::GameThread, [=]() { AWeb3Auth::loginEvent.ExecuteIfBound(web3AuthResponse); }); @@ -347,6 +356,106 @@ void AWeb3Auth::EndPlay(const EEndPlayReason::Type EndPlayReason) { httpRoutes.Empty(); } +void AWeb3Auth::authorizeSession() { + FString sessionId = AWeb3Auth::keyStoreUtils->Get("sessionid"); + if (!sessionId.IsEmpty()) { + FString pubKey = crypto->generatePublicKey(sessionId); + UE_LOG(LogTemp, Log, TEXT("public key %s"), *pubKey); + + web3AuthApi->AuthorizeSession(pubKey, [sessionId](FStoreApiResponse response) + { + UE_LOG(LogTemp, Log, TEXT("Response: %s"), *response.message); + + FShareMetaData shareMetaData; + + if (!FJsonObjectConverter::JsonObjectStringToUStruct(response.message, &shareMetaData, 0, 0)) { + UE_LOG(LogTemp, Error, TEXT("failed to parse json")); + return; + } + + FString output = crypto->decrypt(shareMetaData.ciphertext, sessionId, shareMetaData.ephemPublicKey, shareMetaData.iv); + UE_LOG(LogTemp, Log, TEXT("output %s"), *output); + + TSharedPtr tempJson; + TSharedRef> JsonReader = TJsonReaderFactory::Create(output); + + if (FJsonSerializer::Deserialize(JsonReader, tempJson) && tempJson.IsValid()) { + tempJson->SetObjectField("userInfo", tempJson->GetObjectField("store")); + tempJson->RemoveField("store"); + + FString json; + TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&json); + FJsonSerializer::Serialize(tempJson.ToSharedRef(), Writer); + + FWeb3AuthResponse web3AuthResponse; + + if (!FJsonObjectConverter::JsonObjectStringToUStruct(json, &web3AuthResponse, 0, 0)) { + UE_LOG(LogTemp, Error, TEXT("failed to parse json")); + return; + } + + if (web3AuthResponse.error != "") { + return; + } + + AsyncTask(ENamedThreads::GameThread, [=]() { + AWeb3Auth::loginEvent.ExecuteIfBound(web3AuthResponse); + }); + } + + }); + } +} + +void AWeb3Auth::sessionTimeout() { + FString sessionId = AWeb3Auth::keyStoreUtils->Get("sessionid"); + + if (!sessionId.IsEmpty()) { + FString pubKey = crypto->generatePublicKey(sessionId); + UE_LOG(LogTemp, Log, TEXT("public key %s"), *pubKey); + + web3AuthApi->AuthorizeSession(pubKey, [pubKey, sessionId, this](FStoreApiResponse response) + { + UE_LOG(LogTemp, Log, TEXT("Response: %s"), *response.message); + + FShareMetaData shareMetaData; + + if (!FJsonObjectConverter::JsonObjectStringToUStruct(response.message, &shareMetaData, 0, 0)) { + UE_LOG(LogTemp, Error, TEXT("failed to parse json")); + return; + } + + FString encryptedData = crypto->encrypt("", sessionId, shareMetaData.ephemPublicKey, shareMetaData.iv); + shareMetaData.ciphertext = encryptedData; + + + TSharedPtr jsonObject = MakeShareable(new FJsonObject); + FJsonObjectConverter::UStructToJsonObject(FShareMetaData::StaticStruct(), &shareMetaData, jsonObject.ToSharedRef(), 0, 0); + + FString jsonString; + TSharedRef> jsonWriter = TJsonWriterFactory<>::Create(&jsonString); + FJsonSerializer::Serialize(jsonObject.ToSharedRef(), jsonWriter); + + FLogoutApiRequest request; + request.data = jsonString; + request.key = pubKey; + request.signature = crypto->generateECDSASignature(sessionId, jsonString); + request.timeout = 1; + + web3AuthApi->Logout(request, [](FString response) + { + UE_LOG(LogTemp, Log, TEXT("Response: %s"), *response); + AWeb3Auth::keyStoreUtils->Remove("sessionId"); + AsyncTask(ENamedThreads::GameThread, [=]() { + AWeb3Auth::logoutEvent.ExecuteIfBound(); + }); + }); + }); + + } +} + + void AWeb3Auth::Tick(float DeltaTime) { Super::Tick(DeltaTime); } diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3AuthApi.cpp b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3AuthApi.cpp new file mode 100644 index 0000000..c3f29c3 --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3AuthApi.cpp @@ -0,0 +1,80 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Web3AuthApi.h" +#include "GenericPlatform/GenericPlatformHttp.h" + +UWeb3AuthApi* UWeb3AuthApi::Instance = nullptr; + +UWeb3AuthApi* UWeb3AuthApi::GetInstance() +{ + if (!Instance) { + Instance = NewObject(); + } + return Instance; +} + + +void UWeb3AuthApi::AuthorizeSession(const FString& key, const TFunction callback) +{ + TSharedRef request = FHttpModule::Get().CreateRequest(); + request->SetVerb(TEXT("POST")); + request->SetURL(TEXT("https://broadcast-server.tor.us/store/get?key=" + key)); + + FString FormString = "key=" + key; + + request->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded")); + request->SetContentAsString(FormString); + + request->OnProcessRequestComplete().BindLambda([callback](FHttpRequestPtr request, FHttpResponsePtr response, bool success) { + FString response_string = response->GetContentAsString(); + FString url = request->GetURL(); + UE_LOG(LogTemp, Log, TEXT("Response: %s"), *response_string); + + if (success && response->GetResponseCode() == EHttpResponseCodes::Ok) { + FStoreApiResponse api_response; + if (FJsonObjectConverter::JsonObjectStringToUStruct(response_string, &api_response, 0, 0)) { + callback(api_response); + } + else { + UE_LOG(LogTemp, Error, TEXT("Failed to parse response")); + } + } + else { + UE_LOG(LogTemp, Error, TEXT("Request failed")); + } + }); + + request->ProcessRequest(); +} + +void UWeb3AuthApi::Logout(const FLogoutApiRequest logoutApiRequest, const TFunction callback) +{ + TSharedRef request = FHttpModule::Get().CreateRequest(); + request->SetVerb(TEXT("POST")); + request->SetURL(TEXT("https://broadcast-server.tor.us/store/set")); + + FString FormString = "key=" + logoutApiRequest.key + "&data=" + FGenericPlatformHttp::UrlEncode(logoutApiRequest.data) + "&signature=" + logoutApiRequest.signature + "&timeout=" + FString::FromInt(logoutApiRequest.timeout); + + request->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded")); + request->SetContentAsString(FormString); + + request->OnProcessRequestComplete().BindLambda([callback](FHttpRequestPtr request, FHttpResponsePtr response, bool success) { + FString response_string = response->GetContentAsString(); + UE_LOG(LogTemp, Log, TEXT("Response: %s "), *response_string); + UE_LOG(LogTemp, Log, TEXT("Status code: %d "), response->GetResponseCode()); + + if (success && response->GetResponseCode() == EHttpResponseCodes::Created) { + callback(response_string); + } + else { + UE_LOG(LogTemp, Error, TEXT("Request failed")); + } + }); + + request->ProcessRequest(); +} + +UWeb3AuthApi::UWeb3AuthApi() +{ +} diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/ECCrypto.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/ECCrypto.h new file mode 100644 index 0000000..f6650f9 --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/ECCrypto.h @@ -0,0 +1,45 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#pragma warning(disable:4996) +#pragma comment(lib,"WS2_32.Lib") //Winsock Library + +#define UI UI_ST +THIRD_PARTY_INCLUDES_START +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +THIRD_PARTY_INCLUDES_END +#undef UI + +#include "Misc/SecureHash.h" +#include +#include "Keccak256.h" + +#include "ECCrypto.generated.h" + +UCLASS() +class WEB3AUTHSDK_API UECCrypto : public UObject +{ + GENERATED_BODY() +public: + UECCrypto(); + ~UECCrypto(); + + FString decrypt(FString data, FString privateKeyHex, FString ephemPublicKeyHex, FString encryptionIvHex); + FString encrypt(FString data, FString privateKeyHex, FString ephemPublicKeyHex, FString encryptionIvHex); + + FString generatePublicKey(const FString& privateKeyHex); + FString generateECDSASignature(const FString& privateKeyHex, const FString& data); +}; diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Keccak256.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Keccak256.h new file mode 100644 index 0000000..ccbb170 --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Keccak256.h @@ -0,0 +1,41 @@ +/* + * Bitcoin cryptography library + * Copyright (c) Project Nayuki + * + * https://www.nayuki.io/page/bitcoin-cryptography-library + * https://github.com/nayuki/Bitcoin-Cryptography-Library + */ + +#pragma once + +#include +#include + + + /* + * Computes the Keccak-256 hash of a sequence of bytes. The hash value is 32 bytes long. + * Provides just one static method. + */ +class Keccak256 final { + +public: static constexpr int HASH_LEN = 32; +private: static constexpr int BLOCK_SIZE = 200 - HASH_LEN * 2; +private: static constexpr int NUM_ROUNDS = 24; + + +public: static void getHash(const std::uint8_t msg[], std::size_t len, std::uint8_t hashResult[HASH_LEN]); + + +private: static void absorb(std::uint64_t state[5][5]); + + + // Requires 0 <= i <= 63 +private: static std::uint64_t rotl64(std::uint64_t x, int i); + + + Keccak256() = delete; // Not instantiable + + +private: static const unsigned char ROTATION[5][5]; + +}; \ No newline at end of file diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/KeyStoreUtils.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/KeyStoreUtils.h new file mode 100644 index 0000000..0f7cd4a --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/KeyStoreUtils.h @@ -0,0 +1,33 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/SaveGame.h" +#include "Kismet/GameplayStatics.h" + +#include "KeyStoreUtils.generated.h" + +UCLASS() +class UWeb3StorageAdapter : public USaveGame +{ + GENERATED_BODY() +public: + UPROPERTY() + TMap KeyValuePairs; +}; + +UCLASS() +class WEB3AUTHSDK_API UKeyStoreUtils : public UObject +{ + GENERATED_BODY() +private: + UWeb3StorageAdapter* StorageInstance; +public: + void Add(FString key, FString value); + FString Get(FString key); + void Remove(FString key); +public: + UKeyStoreUtils(); + ~UKeyStoreUtils(); +}; diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h index f98a7d4..1c730f0 100644 --- a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h @@ -10,7 +10,9 @@ #include "JsonUtilities.h" #include "Misc/Base64.h" - +#include "KeyStoreUtils.h" +#include "ECCrypto.h" +#include "Web3AuthApi.h" #include "Runtime/Online/HTTPServer/Public/HttpPath.h" @@ -521,6 +523,25 @@ struct FWeb3AuthResponse }; +USTRUCT(BlueprintType) +struct FShareMetaData +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite) + FString iv; + + UPROPERTY(BlueprintReadWrite) + FString ephemPublicKey; + + UPROPERTY(BlueprintReadWrite) + FString ciphertext; + + UPROPERTY(BlueprintReadWrite) + FString mac; +}; + DECLARE_DYNAMIC_DELEGATE_OneParam(FOnLogin, FWeb3AuthResponse, response); DECLARE_DYNAMIC_DELEGATE(FOnLogout); @@ -539,6 +560,12 @@ class WEB3AUTHSDK_API AWeb3Auth : public AActor static FOnLogin loginEvent; static FOnLogout logoutEvent; + + static UKeyStoreUtils* keyStoreUtils; + static UECCrypto* crypto; + + UWeb3AuthApi* web3AuthApi = UWeb3AuthApi::GetInstance(); + protected: // Called when the game starts or when spawned virtual void BeginPlay() override; @@ -600,4 +627,7 @@ class WEB3AUTHSDK_API AWeb3Auth : public AActor bool requestAuthCallback(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete); bool requestCompleteCallback(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete); + + void authorizeSession(); + void sessionTimeout(); }; diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h new file mode 100644 index 0000000..5198b91 --- /dev/null +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h @@ -0,0 +1,61 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Http.h" +#include "JsonUtilities.h" + +#include "Web3AuthApi.generated.h" + +USTRUCT() +struct FStoreApiResponse +{ + GENERATED_BODY() + + UPROPERTY() + FString message; + + UPROPERTY() + bool success; +}; + +USTRUCT() +struct FLogoutApiRequest +{ + GENERATED_BODY() + + UPROPERTY() + FString key; + + UPROPERTY() + FString data; + + UPROPERTY() + FString signature; + + UPROPERTY() + int32 timeout; +}; + +UCLASS() +class WEB3AUTHSDK_API UWeb3AuthApi : public UObject +{ + GENERATED_BODY() + +public: + static UWeb3AuthApi* GetInstance(); + + // Authorize the user session + void AuthorizeSession(const FString& key, const TFunction callback); + + // Logout the user session + void Logout(const FLogoutApiRequest logoutApiRequest, const TFunction callback); + +private: + // Private constructor to enforce singleton pattern + UWeb3AuthApi(); + + // Singleton instance of the Web3AuthApi class + static UWeb3AuthApi* Instance; +}; \ No newline at end of file diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Web3AuthSDK.Build.cs b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Web3AuthSDK.Build.cs index 5affc4c..dfd4db8 100644 --- a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Web3AuthSDK.Build.cs +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Web3AuthSDK.Build.cs @@ -60,6 +60,7 @@ public Web3AuthSDK(ReadOnlyTargetRules Target) : base(Target) { "Core", // ... add other public dependencies that you statically link with here ... + "OpenSSL" } ); @@ -79,14 +80,33 @@ public Web3AuthSDK(ReadOnlyTargetRules Target) : base(Target) "UMG" } ); - - - DynamicallyLoadedModuleNames.AddRange( + + if (Target.Platform == UnrealTargetPlatform.Win64) + { + PrivateDependencyModuleNames.Add("OpenSSL"); + + if (Target.LinkType == TargetLinkType.Monolithic) + { + PrivateDependencyModuleNames.Add("ws2_32"); + } + else + { + PublicAdditionalLibraries.Add("ws2_32.lib"); + } + } + + DynamicallyLoadedModuleNames.AddRange( new string[] { // ... add any modules that your module loads dynamically here ... } ); - } + PublicDefinitions.AddRange( + new string[] + { + } + ); + + } }