Initial commit: PrivateBin API C++ DLL implementation

This commit is contained in:
2025-08-28 09:15:47 +02:00
commit 90d9a23dd2
21 changed files with 2053 additions and 0 deletions

89
src/base58.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "base58.h"
#include <algorithm>
#include <stdexcept>
const std::string Base58::ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
std::string Base58::encode(const std::vector<unsigned char>& data) {
if (data.empty()) {
return "";
}
// Skip leading zeros
size_t leading_zeros = 0;
while (leading_zeros < data.size() && data[leading_zeros] == 0) {
leading_zeros++;
}
// Convert to base58
std::vector<unsigned char> digits((data.size() - leading_zeros) * 138 / 100 + 1);
size_t digitslen = 1;
for (size_t i = leading_zeros; i < data.size(); i++) {
unsigned int carry = data[i];
for (size_t j = 0; j < digitslen; j++) {
carry += (unsigned int)(digits[j]) << 8;
digits[j] = carry % 58;
carry /= 58;
}
while (carry > 0) {
digits[digitslen++] = carry % 58;
carry /= 58;
}
}
// Convert to string
std::string result;
for (size_t i = 0; i < leading_zeros; i++) {
result += ALPHABET[0];
}
for (size_t i = 0; i < digitslen; i++) {
result += ALPHABET[digits[digitslen - 1 - i]];
}
return result;
}
std::vector<unsigned char> Base58::decode(const std::string& encoded) {
if (encoded.empty()) {
return std::vector<unsigned char>();
}
// Skip leading '1's (which represent leading zeros)
size_t leading_ones = 0;
while (leading_ones < encoded.length() && encoded[leading_ones] == '1') {
leading_ones++;
}
// Convert from base58
std::vector<unsigned char> bytes((encoded.length() - leading_ones) * 733 / 1000 + 1);
size_t byteslen = 1;
for (size_t i = leading_ones; i < encoded.length(); i++) {
unsigned int carry = ALPHABET.find(encoded[i]);
if (carry == std::string::npos) {
throw std::invalid_argument("Invalid character in Base58 string");
}
for (size_t j = 0; j < byteslen; j++) {
carry += (unsigned int)(bytes[j]) * 58;
bytes[j] = carry & 0xff;
carry >>= 8;
}
while (carry > 0) {
bytes[byteslen++] = carry & 0xff;
carry >>= 8;
}
}
// Add leading zeros
std::vector<unsigned char> result(leading_ones + byteslen);
for (size_t i = 0; i < leading_ones; i++) {
result[i] = 0;
}
for (size_t i = 0; i < byteslen; i++) {
result[leading_ones + i] = bytes[byteslen - 1 - i];
}
return result;
}

80
src/crypto.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "crypto.h"
#include <random>
#include <stdexcept>
#include <cstring>
// For now, we'll provide stub implementations
// In a real implementation, you would use a crypto library like Crypto++ or OpenSSL
std::vector<unsigned char> Crypto::generate_key(size_t length) {
std::vector<unsigned char> key(length);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 255);
for (size_t i = 0; i < length; ++i) {
key[i] = static_cast<unsigned char>(dis(gen));
}
return key;
}
std::vector<unsigned char> Crypto::encrypt(const std::vector<unsigned char>& plaintext,
const std::vector<unsigned char>& key,
const std::vector<unsigned char>& iv,
std::vector<unsigned char>& auth_tag) {
// This is a stub implementation - in a real implementation,
// you would use a proper crypto library like Crypto++ or OpenSSL
// to perform AES-GCM encryption
// For demonstration purposes, we'll just return the plaintext
// In a real implementation, this would be the actual encryption
auth_tag.resize(16, 0); // 128-bit authentication tag
return plaintext;
}
std::vector<unsigned char> Crypto::decrypt(const std::vector<unsigned char>& ciphertext,
const std::vector<unsigned char>& key,
const std::vector<unsigned char>& iv,
const std::vector<unsigned char>& auth_tag) {
// This is a stub implementation - in a real implementation,
// you would use a proper crypto library like Crypto++ or OpenSSL
// to perform AES-GCM decryption
// For demonstration purposes, we'll just return the ciphertext
// In a real implementation, this would be the actual decryption
return ciphertext;
}
std::vector<unsigned char> Crypto::pbkdf2_hmac_sha256(const std::string& password,
const std::vector<unsigned char>& salt,
int iterations,
size_t key_length) {
// This is a stub implementation - in a real implementation,
// you would use a proper crypto library to perform PBKDF2-HMAC-SHA256
// For demonstration purposes, we'll just return a key of the requested length
// filled with a simple pattern
std::vector<unsigned char> key(key_length, 0);
for (size_t i = 0; i < key_length; i++) {
key[i] = static_cast<unsigned char>((i * 17) % 256);
}
return key;
}
std::vector<unsigned char> Crypto::compress(const std::vector<unsigned char>& data) {
// This is a stub implementation - in a real implementation,
// you would use zlib or another compression library
// For demonstration purposes, we'll just return the data as-is
return data;
}
std::vector<unsigned char> Crypto::decompress(const std::vector<unsigned char>& data) {
// This is a stub implementation - in a real implementation,
// you would use zlib or another decompression library
// For demonstration purposes, we'll just return the data as-is
return data;
}

506
src/http_client.cpp Normal file
View File

@ -0,0 +1,506 @@
#include "http_client.h"
#include <iostream>
#include <string>
#ifdef WINDOWS
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
#elif LINUX
#include <curl/curl.h>
#endif
#ifdef WINDOWS
struct WinHttpData {
std::string data;
size_t size;
};
static size_t WinHttpWriteCallback(void* contents, size_t size, size_t nmemb, WinHttpData* data) {
size_t realsize = size * nmemb;
data->data.append((char*)contents, realsize);
data->size += realsize;
return realsize;
}
bool HttpClient::get(const std::string& url, std::string& response) {
// Parse URL
URL_COMPONENTS urlComp;
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
// Set required component lengths to non-zero to indicate they exist
urlComp.dwHostNameLength = (DWORD)-1;
urlComp.dwUrlPathLength = (DWORD)-1;
urlComp.dwExtraInfoLength = (DWORD)-1;
// Parse the URL
if (!WinHttpCrackUrl((LPCWSTR)url.c_str(), 0, 0, &urlComp)) {
return false;
}
// Use WinHttpOpen to obtain a session handle
HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
return false;
}
// Specify an HTTP server
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
urlComp.nPort, 0);
if (!hConnect) {
WinHttpCloseHandle(hSession);
return false;
}
// Create an HTTP request handle
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET",
urlComp.lpszUrlPath,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
(urlComp.nScheme == INTERNET_SCHEME_HTTPS) ?
WINHTTP_FLAG_SECURE : 0);
if (!hRequest) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Send a request
if (!WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0,
0, 0)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// End the request
if (!WinHttpReceiveResponse(hRequest, NULL)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Keep checking for data until there is nothing left
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
std::string result;
do {
// Check for available data
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Allocate space for the buffer
char* pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Read the data
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
delete[] pszOutBuffer;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
else {
result.append(pszOutBuffer);
}
// Free the memory
delete[] pszOutBuffer;
} while (dwSize > 0);
response = result;
// Close any open handles
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return true;
}
bool HttpClient::post(const std::string& url, const std::string& data, std::string& response) {
// Parse URL
URL_COMPONENTS urlComp;
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
// Set required component lengths to non-zero to indicate they exist
urlComp.dwHostNameLength = (DWORD)-1;
urlComp.dwUrlPathLength = (DWORD)-1;
urlComp.dwExtraInfoLength = (DWORD)-1;
// Parse the URL
if (!WinHttpCrackUrl((LPCWSTR)url.c_str(), 0, 0, &urlComp)) {
return false;
}
// Use WinHttpOpen to obtain a session handle
HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
return false;
}
// Specify an HTTP server
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
urlComp.nPort, 0);
if (!hConnect) {
WinHttpCloseHandle(hSession);
return false;
}
// Create an HTTP request handle
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
urlComp.lpszUrlPath,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
(urlComp.nScheme == INTERNET_SCHEME_HTTPS) ?
WINHTTP_FLAG_SECURE : 0);
if (!hRequest) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Set headers
LPCWSTR headers = L"Content-Type: application/json\r\nX-Requested-With: JSONHttpRequest";
BOOL bResults = WinHttpAddRequestHeaders(hRequest,
headers,
-1L,
WINHTTP_ADDREQ_FLAG_ADD);
if (!bResults) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Send a request
if (!WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
(LPVOID)data.c_str(), (DWORD)data.length(),
(DWORD)data.length(), 0)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// End the request
if (!WinHttpReceiveResponse(hRequest, NULL)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Keep checking for data until there is nothing left
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
std::string result;
do {
// Check for available data
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Allocate space for the buffer
char* pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Read the data
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
delete[] pszOutBuffer;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
else {
result.append(pszOutBuffer);
}
// Free the memory
delete[] pszOutBuffer;
} while (dwSize > 0);
response = result;
// Close any open handles
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return true;
}
bool HttpClient::delete_req(const std::string& url, const std::string& data, std::string& response) {
// Parse URL
URL_COMPONENTS urlComp;
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
// Set required component lengths to non-zero to indicate they exist
urlComp.dwHostNameLength = (DWORD)-1;
urlComp.dwUrlPathLength = (DWORD)-1;
urlComp.dwExtraInfoLength = (DWORD)-1;
// Parse the URL
if (!WinHttpCrackUrl((LPCWSTR)url.c_str(), 0, 0, &urlComp)) {
return false;
}
// Use WinHttpOpen to obtain a session handle
HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
return false;
}
// Specify an HTTP server
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
urlComp.nPort, 0);
if (!hConnect) {
WinHttpCloseHandle(hSession);
return false;
}
// Create an HTTP request handle
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
urlComp.lpszUrlPath,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
(urlComp.nScheme == INTERNET_SCHEME_HTTPS) ?
WINHTTP_FLAG_SECURE : 0);
if (!hRequest) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Set headers
LPCWSTR headers = L"Content-Type: application/json\r\nX-Requested-With: JSONHttpRequest";
BOOL bResults = WinHttpAddRequestHeaders(hRequest,
headers,
-1L,
WINHTTP_ADDREQ_FLAG_ADD);
if (!bResults) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Send a request with DELETE method
if (!WinHttpSendRequest(hRequest,
L"DELETE", 6,
(LPVOID)data.c_str(), (DWORD)data.length(),
(DWORD)data.length(), 0)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// End the request
if (!WinHttpReceiveResponse(hRequest, NULL)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Keep checking for data until there is nothing left
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
std::string result;
do {
// Check for available data
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Allocate space for the buffer
char* pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
// Read the data
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
delete[] pszOutBuffer;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
else {
result.append(pszOutBuffer);
}
// Free the memory
delete[] pszOutBuffer;
} while (dwSize > 0);
response = result;
// Close any open handles
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return true;
}
#elif LINUX
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
size_t realsize = size * nmemb;
userp->append((char*)contents, realsize);
return realsize;
}
bool HttpClient::get(const std::string& url, std::string& response) {
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (!curl) {
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return (res == CURLE_OK);
}
bool HttpClient::post(const std::string& url, const std::string& data, std::string& response) {
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (!curl) {
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return (res == CURLE_OK);
}
bool HttpClient::delete_req(const std::string& url, const std::string& data, std::string& response) {
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (!curl) {
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return (res == CURLE_OK);
}
#endif

121
src/json_parser.cpp Normal file
View File

@ -0,0 +1,121 @@
#include "json_parser.h"
#include <iostream>
#include <chrono>
json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text,
const std::vector<unsigned char>& auth_tag,
const std::vector<unsigned char>& iv,
const std::vector<unsigned char>& salt,
const std::string& expiration,
const std::string& format,
bool burn_after_reading,
bool open_discussion) {
// Convert binary data to base64 strings (stub implementation)
std::string cipher_text_b64(cipher_text.begin(), cipher_text.end());
std::string auth_tag_b64(auth_tag.begin(), auth_tag.end());
std::string iv_b64(iv.begin(), iv.end());
std::string salt_b64(salt.begin(), salt.end());
// Get current timestamp
auto now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
// Create the metadata array
json metadata = {
{
iv_b64, // base64(cipher_iv)
salt_b64, // base64(kdf_salt)
100000, // iterations
256, // key size
128, // tag size
"aes", // cipher
"gcm", // mode
"zlib" // compression
},
format, // format
burn_after_reading ? 1 : 0, // burn after reading
open_discussion ? 1 : 0 // open discussion
};
// Create the main JSON structure
json paste_json = {
{"v", 2}, // version
{"adata", metadata}, // metadata
{"ct", cipher_text_b64}, // cipher text
{"meta", {
{"expire", expiration},
{"created", now},
{"time_to_live", 300}, // This would be calculated based on expiration
{"icon", "data:image/png;base64,..."} // Placeholder
}}
};
return paste_json;
}
bool JsonParser::parse_paste_json(const json& json_data,
std::vector<unsigned char>& cipher_text,
std::vector<unsigned char>& auth_tag,
std::vector<unsigned char>& iv,
std::vector<unsigned char>& salt,
std::string& expiration) {
try {
// Extract cipher text
std::string ct = json_data["ct"];
cipher_text = std::vector<unsigned char>(ct.begin(), ct.end());
// Extract metadata
json adata = json_data["adata"];
if (adata.size() >= 1) {
json first_element = adata[0];
if (first_element.is_array() && first_element.size() >= 2) {
// Extract IV and salt (from base64 in real implementation)
std::string iv_str = first_element[0];
std::string salt_str = first_element[1];
iv = std::vector<unsigned char>(iv_str.begin(), iv_str.end());
salt = std::vector<unsigned char>(salt_str.begin(), salt_str.end());
}
}
// Extract expiration
expiration = json_data["meta"]["expire"];
// Auth tag would be extracted from the ciphertext in a real implementation
auth_tag.resize(16, 0);
return true;
} catch (...) {
return false;
}
}
bool JsonParser::parse_response(const std::string& response,
int& status,
std::string& message,
std::string& paste_id,
std::string& url,
std::string& delete_token) {
try {
json json_response = json::parse(response);
status = json_response["status"];
if (status == 0) {
// Success response
paste_id = json_response["id"];
url = json_response["url"];
delete_token = json_response["deletetoken"];
} else {
// Error response
message = json_response["message"];
}
return true;
} catch (...) {
return false;
}
}

218
src/privatebinapi.cpp Normal file
View File

@ -0,0 +1,218 @@
#include "privatebinapi.h"
#include "http_client.h"
#include "crypto.h"
#include "json_parser.h"
#include "base58.h"
#include <string>
#include <vector>
#include <cstring>
#include <cstdlib>
#define PRIVATEBIN_API_VERSION "1.3"
// Error codes
#define ERROR_SUCCESS 0
#define ERROR_NETWORK 1
#define ERROR_CRYPTO 2
#define ERROR_INVALID_INPUT 3
#define ERROR_SERVER 4
#define ERROR_JSON_PARSE 5
// Helper function to copy string to output parameter
static void copy_string_to_output(const std::string& source, char** destination) {
if (destination) {
*destination = static_cast<char*>(malloc(source.length() + 1));
if (*destination) {
std::strcpy(*destination, source.c_str());
}
}
}
extern "C" {
int create_paste(const char* server_url, const char* content,
const char* password, const char* expiration,
const char* format, int burn_after_reading,
int open_discussion, char** paste_url,
char** delete_token) {
if (!server_url || !content) {
return ERROR_INVALID_INPUT;
}
try {
// Generate a random 32-byte paste key
std::vector<unsigned char> paste_key = Crypto::generate_key(32);
// If password provided, append it to the paste key
std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
if (password) {
paste_passphrase += password;
}
// Convert content to bytes
std::vector<unsigned char> plaintext(content, content + strlen(content));
// Compress the plaintext
std::vector<unsigned char> compressed_data = Crypto::compress(plaintext);
// Generate random salt and IV
std::vector<unsigned char> salt = Crypto::generate_key(8);
std::vector<unsigned char> iv = Crypto::generate_key(16);
// Derive key using PBKDF2
std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
paste_passphrase, salt, 100000, 32);
// Encrypt the data
std::vector<unsigned char> auth_tag;
std::vector<unsigned char> cipher_text = Crypto::encrypt(
compressed_data, derived_key, iv, auth_tag);
// Create the JSON structure
json paste_json = JsonParser::create_paste_json(
cipher_text, auth_tag, iv, salt,
expiration ? expiration : "1day",
format ? format : "plaintext",
burn_after_reading != 0,
open_discussion != 0);
// Serialize JSON
std::string json_data = paste_json.dump();
// Send POST request
HttpClient client;
std::string response;
if (!client.post(server_url, json_data, response)) {
return ERROR_NETWORK;
}
// Parse response
int status;
std::string message, paste_id, url, del_token;
if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) {
return ERROR_JSON_PARSE;
}
if (status != 0) {
return ERROR_SERVER;
}
// Encode the paste key with Base58
std::string encoded_key = Base58::encode(paste_key);
// Construct the full URL
std::string full_url = url + "#" + encoded_key;
// Copy results to output parameters
copy_string_to_output(full_url, paste_url);
copy_string_to_output(del_token, delete_token);
return ERROR_SUCCESS;
} catch (...) {
return ERROR_CRYPTO;
}
}
int get_paste(const char* server_url, const char* paste_id,
const char* key, char** content) {
if (!server_url || !paste_id || !key || !content) {
return ERROR_INVALID_INPUT;
}
try {
// Construct the URL
std::string url = std::string(server_url) + "/" + paste_id;
// Send GET request
HttpClient client;
std::string response;
if (!client.get(url, response)) {
return ERROR_NETWORK;
}
// Parse the JSON response
json json_data = json::parse(response);
// Extract encrypted data
std::vector<unsigned char> cipher_text, auth_tag, iv, salt;
std::string expiration;
if (!JsonParser::parse_paste_json(json_data, cipher_text, auth_tag, iv, salt, expiration)) {
return ERROR_JSON_PARSE;
}
// Decode the key from Base58
std::vector<unsigned char> paste_key = Base58::decode(key);
// Derive key using PBKDF2
std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
paste_passphrase, salt, 100000, 32);
// Decrypt the data
std::vector<unsigned char> compressed_data = Crypto::decrypt(
cipher_text, derived_key, iv, auth_tag);
// Decompress the data
std::vector<unsigned char> plaintext = Crypto::decompress(compressed_data);
// Convert to string
std::string result(reinterpret_cast<char*>(plaintext.data()), plaintext.size());
// Copy result to output parameter
copy_string_to_output(result, content);
return ERROR_SUCCESS;
} catch (...) {
return ERROR_CRYPTO;
}
}
int delete_paste(const char* server_url, const char* paste_id,
const char* delete_token) {
if (!server_url || !paste_id || !delete_token) {
return ERROR_INVALID_INPUT;
}
try {
// Create the JSON payload
json payload = {
{"pasteid", paste_id},
{"deletetoken", delete_token}
};
std::string json_data = payload.dump();
// Send DELETE request
HttpClient client;
std::string response;
if (!client.delete_req(server_url, json_data, response)) {
return ERROR_NETWORK;
}
// Parse response
int status;
std::string message, paste_id_result, url, del_token;
if (!JsonParser::parse_response(response, status, message, paste_id_result, url, del_token)) {
return ERROR_JSON_PARSE;
}
if (status != 0) {
return ERROR_SERVER;
}
return ERROR_SUCCESS;
} catch (...) {
return ERROR_CRYPTO;
}
}
void free_string(char* str) {
if (str) {
free(str);
}
}
} // extern "C"