Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -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
230 changes: 230 additions & 0 deletions Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/ECCrypto.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
#include "ECCrypto.h"
#include <string>
#include <vector>
#include <Web3Auth.h>

#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()
{
}


101 changes: 101 additions & 0 deletions Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Keccak256.cpp
Original file line number Diff line number Diff line change
@@ -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 <cassert>

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<uint64_t>(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<uint8_t>(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<uint64_t>(r & 1) << ((1 << j) - 1);
r = static_cast<uint8_t>((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},
};
30 changes: 30 additions & 0 deletions Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/KeyStoreUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "KeyStoreUtils.h"

UKeyStoreUtils::UKeyStoreUtils() {
StorageInstance = Cast<UWeb3StorageAdapter>(UGameplayStatics::LoadGameFromSlot(TEXT("Web3AuthDataSlot"), 0));
if(StorageInstance == nullptr) {
StorageInstance = Cast<UWeb3StorageAdapter>(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);
}
}
Loading