Release v0.1.8.4 prepare for release

This commit is contained in:
2025-08-29 13:12:32 +02:00
parent 4f32e0a2fd
commit eb73749367
76 changed files with 921 additions and 1011 deletions

356
src/libprivatebin.cpp Normal file
View File

@ -0,0 +1,356 @@
#include "libprivatebin.h"
#include "http_client.h"
#include "crypto.h"
#include "json_parser.h"
#include "base58.h"
#include <string>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#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) {
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<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::cerr << "[privatebinapi] compress..." << std::endl;
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::cerr << "[privatebinapi] pbkdf2..." << std::endl;
std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
paste_passphrase, salt, 100000, 32);
// Encrypt the data
std::cerr << "[privatebinapi] encrypt..." << std::endl;
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)) {
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<unsigned char> file_content(file_size);
if (!file.read(reinterpret_cast<char*>(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<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;
}
// Compress the file content
std::cerr << "[privatebinapi] compress file..." << std::endl;
std::vector<unsigned char> compressed_data = Crypto::compress(file_content);
// 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::cerr << "[privatebinapi] pbkdf2..." << std::endl;
std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
paste_passphrase, salt, 100000, 32);
// Encrypt the data
std::cerr << "[privatebinapi] encrypt file..." << std::endl;
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 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<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;
}
}
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"