Implement RSA keypair management for username signature authentication in OverteAuth

This commit is contained in:
MayaTheShy
2025-11-16 19:45:25 -05:00
parent c2fec07ad6
commit db3a2e2a59
2 changed files with 178 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
// OverteAuth.cpp - Enhanced OAuth implementation with browser flow
#include "OverteAuth.hpp"
#include "RSAKeypair.hpp"
#include <iostream>
#include <sstream>
@@ -24,7 +25,7 @@
using namespace std::chrono;
OverteAuth::OverteAuth() {
OverteAuth::OverteAuth() : m_keypair(std::make_unique<RSAKeypair>()) {
curl_global_init(CURL_GLOBAL_DEFAULT);
// Try to load saved token
@@ -697,3 +698,166 @@ bool OverteAuth::loginWithBrowser(const std::string& metaverseUrl) {
std::cout << "[OverteAuth] Exchanging authorization code for access token..." << std::endl;
return loginWithAuthCode(m_receivedAuthCode, getCallbackURL());
}
// ============================================================================
// RSA Keypair Management
// ============================================================================
bool OverteAuth::generateKeypair() {
if (!m_keypair) {
m_keypair = std::make_unique<RSAKeypair>();
}
std::cout << "[OverteAuth] Generating RSA keypair for username signature..." << std::endl;
return m_keypair->generate();
}
bool OverteAuth::uploadPublicKey() {
if (!m_keypair || !m_keypair->isValid()) {
m_lastError = "No keypair generated";
return false;
}
if (!isAuthenticated()) {
m_lastError = "Must be authenticated to upload public key";
return false;
}
std::string url = m_metaverseUrl;
if (url.back() == '/') url.pop_back();
if (url.find("/server") == std::string::npos) {
url += "/server";
}
url += "/api/v1/user/public_key";
// Get public key in DER format
auto publicKeyDER = m_keypair->getPublicKeyDER();
// Base64 encode for transmission
auto base64Encode = [](const std::vector<uint8_t>& in) {
static const char* tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string out;
out.reserve(((in.size() + 2) / 3) * 4);
size_t i = 0;
while (i < in.size()) {
uint32_t val = 0;
int bytes = 0;
for (int j = 0; j < 3; ++j) {
val <<= 8;
if (i < in.size()) {
val |= in[i++];
++bytes;
}
}
int pad = 3 - bytes;
for (int k = 0; k < 4 - pad; ++k) {
int idx = (val >> (18 - k * 6)) & 0x3F;
out.push_back(tbl[idx]);
}
for (int k = 0; k < pad; ++k) out.push_back('=');
}
return out;
};
std::string publicKeyBase64 = base64Encode(publicKeyDER);
std::ostringstream postData;
postData << "public_key=" << urlEncode(publicKeyBase64);
std::cout << "[OverteAuth] Uploading public key to metaverse..." << std::endl;
CURL* curl = curl_easy_init();
if (!curl) {
m_lastError = "Failed to initialize CURL";
return false;
}
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
// Add authorization header
std::string authHeader = "Authorization: Bearer " + m_accessToken;
headers = curl_slist_append(headers, authHeader.c_str());
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.str().c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
std::string response;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
long httpCode = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
m_lastError = std::string("Public key upload failed: ") + curl_easy_strerror(res);
return false;
}
if (httpCode != 200) {
m_lastError = "Public key upload failed with HTTP " + std::to_string(httpCode);
std::cerr << "[OverteAuth] Server response: " << response << std::endl;
return false;
}
std::cout << "[OverteAuth] Public key uploaded successfully" << std::endl;
return true;
}
bool OverteAuth::hasKeypair() const {
return m_keypair && m_keypair->isValid();
}
std::vector<uint8_t> OverteAuth::getUsernameSignature(const std::string& connectionToken) const {
if (!hasKeypair()) {
std::cerr << "[OverteAuth] Cannot generate signature: no keypair" << std::endl;
return {};
}
// Create plaintext: lowercase_username + connectionToken (UUID bytes)
std::string lowercaseUsername = m_username;
for (char& c : lowercaseUsername) {
c = std::tolower(c);
}
// Parse connection token as UUID and get RFC4122 bytes
// Format: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
std::vector<uint8_t> plaintext(lowercaseUsername.begin(), lowercaseUsername.end());
// Parse UUID string to bytes (16 bytes in RFC4122 format)
auto parseUUID = [](const std::string& uuidStr) -> std::vector<uint8_t> {
std::vector<uint8_t> bytes;
if (uuidStr.length() != 36) return bytes; // Invalid format
std::string hex = uuidStr;
hex.erase(std::remove(hex.begin(), hex.end(), '-'), hex.end());
for (size_t i = 0; i < hex.length(); i += 2) {
std::string byteStr = hex.substr(i, 2);
uint8_t byte = static_cast<uint8_t>(std::stoul(byteStr, nullptr, 16));
bytes.push_back(byte);
}
return bytes;
};
auto tokenBytes = parseUUID(connectionToken);
if (tokenBytes.empty()) {
std::cerr << "[OverteAuth] Invalid connection token format" << std::endl;
return {};
}
plaintext.insert(plaintext.end(), tokenBytes.begin(), tokenBytes.end());
std::cout << "[OverteAuth] Signing username '" << m_username
<< "' with connection token " << connectionToken << std::endl;
return m_keypair->sign(plaintext);
}

View File

@@ -7,6 +7,10 @@
#include <atomic>
#include <memory>
#include <thread>
#include <vector>
// Forward declaration
class RSAKeypair;
// Simple OAuth2 authentication for Overte metaverse
class OverteAuth {
@@ -46,6 +50,12 @@ public:
bool loadTokenFromFile();
bool saveTokenToFile();
// RSA Keypair management (for username signature authentication)
bool generateKeypair();
bool uploadPublicKey();
bool hasKeypair() const;
std::vector<uint8_t> getUsernameSignature(const std::string& connectionToken) const;
private:
std::string m_metaverseUrl;
std::string m_accessToken;
@@ -56,6 +66,9 @@ private:
std::string m_clientId = "starworld";
std::string m_clientSecret = ""; // Public client
// RSA keypair for signature authentication
std::unique_ptr<RSAKeypair> m_keypair;
// OAuth callback HTTP server
int m_callbackServerFd = -1;
int m_callbackPort = 0;