#include "libprivatebin.h" #include "http_client.h" #include "crypto.h" #include "json_parser.h" #include "base58.h" #include #include #include #include #include #include #include #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(malloc(source.length() + 1)); if (*destination) { strcpy_s(*destination, source.length() + 1, source.c_str()); } } } extern "C" { PRIVATEBIN_API 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 paste_key = Crypto::generate_key(32); // If password provided, append it to the paste key std::string paste_passphrase(reinterpret_cast(paste_key.data()), paste_key.size()); if (password) { paste_passphrase += password; } // Convert content to bytes std::vector plaintext(content, content + strlen(content)); // Compress the plaintext std::cerr << "[privatebinapi] compress..." << std::endl; std::vector compressed_data = Crypto::compress(plaintext); // Generate random salt and IV std::vector salt = Crypto::generate_key(8); std::vector iv = Crypto::generate_key(16); // Derive key using PBKDF2 std::cerr << "[privatebinapi] pbkdf2..." << std::endl; std::vector derived_key = Crypto::pbkdf2_hmac_sha256( paste_passphrase, salt, 100000, 32); // Encrypt the data std::cerr << "[privatebinapi] encrypt..." << std::endl; std::vector auth_tag; std::vector 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)) { std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl; return ERROR_JSON_PARSE; } if (status != 0) { std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl; } 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 (const std::exception& e) { std::cerr << "[privatebinapi] crypto error: " << e.what() << std::endl; return ERROR_CRYPTO; } catch (...) { std::cerr << "[privatebinapi] unknown crypto error" << std::endl; return ERROR_CRYPTO; } } PRIVATEBIN_API int upload_file(const char* server_url, const char* file_path, const char* password, const char* expiration, int burn_after_reading, int open_discussion, char** paste_url, char** delete_token) { if (!server_url || !file_path) { return ERROR_INVALID_INPUT; } try { // Read the file content std::ifstream file(file_path, std::ios::binary); if (!file.is_open()) { std::cerr << "[privatebinapi] Failed to open file: " << file_path << std::endl; return ERROR_INVALID_INPUT; } // Get file size file.seekg(0, std::ios::end); std::streamsize file_size = file.tellg(); file.seekg(0, std::ios::beg); // Check if file is too large (PrivateBin typically has limits) if (file_size > 100 * 1024 * 1024) { // 100MB limit std::cerr << "[privatebinapi] File too large: " << file_size << " bytes" << std::endl; return ERROR_INVALID_INPUT; } // Read file content into vector std::vector file_content(file_size); if (!file.read(reinterpret_cast(file_content.data()), file_size)) { std::cerr << "[privatebinapi] Failed to read file content" << std::endl; return ERROR_INVALID_INPUT; } file.close(); // Generate a random 32-byte paste key std::vector paste_key = Crypto::generate_key(32); // If password provided, append it to the paste key std::string paste_passphrase(reinterpret_cast(paste_key.data()), paste_key.size()); if (password) { paste_passphrase += password; } // Compress the file content std::cerr << "[privatebinapi] compress file..." << std::endl; std::vector compressed_data = Crypto::compress(file_content); // Generate random salt and IV std::vector salt = Crypto::generate_key(8); std::vector iv = Crypto::generate_key(16); // Derive key using PBKDF2 std::cerr << "[privatebinapi] pbkdf2..." << std::endl; std::vector derived_key = Crypto::pbkdf2_hmac_sha256( paste_passphrase, salt, 100000, 32); // Encrypt the data std::cerr << "[privatebinapi] encrypt file..." << std::endl; std::vector auth_tag; std::vector cipher_text = Crypto::encrypt( compressed_data, derived_key, iv, auth_tag); // Create the JSON structure for file upload json paste_json = JsonParser::create_paste_json( cipher_text, auth_tag, iv, salt, expiration ? expiration : "1day", "plaintext", // Files are treated as plaintext burn_after_reading != 0, open_discussion != 0); // Note: File metadata is not added to avoid server compatibility issues // The file content is encrypted and uploaded as a binary paste // 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)) { std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl; return ERROR_JSON_PARSE; } if (status != 0) { std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl; } 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 (const std::exception& e) { std::cerr << "[privatebinapi] file upload error: " << e.what() << std::endl; return ERROR_CRYPTO; } catch (...) { std::cerr << "[privatebinapi] unknown file upload error" << std::endl; return ERROR_CRYPTO; } } PRIVATEBIN_API 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 with query per API: base?pasteID std::string url = std::string(server_url) + "?" + paste_id; // Send GET request HttpClient client; std::string response; std::cerr << "[privatebinapi] GET " << url << std::endl; if (!client.get(url, response)) { return ERROR_NETWORK; } std::cerr << "[privatebinapi] GET response: " << response << std::endl; // Parse the JSON response json json_data = json::parse(response); // Extract encrypted data std::vector 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 paste_key = Base58::decode(key); // Derive key using PBKDF2 std::string paste_passphrase(reinterpret_cast(paste_key.data()), paste_key.size()); std::vector derived_key = Crypto::pbkdf2_hmac_sha256( paste_passphrase, salt, 100000, 32); // Decrypt the data std::vector compressed_data = Crypto::decrypt( cipher_text, derived_key, iv, auth_tag); // Decompress the data std::vector plaintext = Crypto::decompress(compressed_data); // Convert to string std::string result(reinterpret_cast(plaintext.data()), plaintext.size()); // Copy result to output parameter copy_string_to_output(result, content); return ERROR_SUCCESS; } catch (...) { return ERROR_CRYPTO; } } PRIVATEBIN_API 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; std::cerr << "[privatebinapi] DELETE payload: " << json_data << std::endl; if (!client.delete_req(server_url, json_data, response)) { return ERROR_NETWORK; } std::cerr << "[privatebinapi] DELETE response: " << response << std::endl; // 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; } } PRIVATEBIN_API void free_string(char* str) { if (str) { free(str); } } } // extern "C"