Files
lib-privatebin/src/json_parser.cpp

149 lines
5.3 KiB
C++

#include "json_parser.h"
#include <iostream>
#include <chrono>
#include <cryptopp/base64.h>
#include <cryptopp/filters.h>
#include <cryptopp/queue.h>
// Helper: Base64 encode without line breaks
static std::string encode_base64(const std::vector<unsigned char>& data) {
std::string result;
CryptoPP::StringSource ss(
data.data(), data.size(), true,
new CryptoPP::Base64Encoder(new CryptoPP::StringSink(result), false /* insertLineBreaks */)
);
return result;
}
// Helper: Base64 decode
static std::vector<unsigned char> decode_base64(const std::string& b64) {
std::string decoded;
CryptoPP::StringSource ss(
b64, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decoded))
);
return std::vector<unsigned char>(decoded.begin(), decoded.end());
}
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
std::string cipher_text_b64 = encode_base64(cipher_text);
std::string iv_b64 = encode_base64(iv);
std::string salt_b64 = encode_base64(salt);
// 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 according to PrivateBin v2
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, // formatter key
burn_after_reading ? 1 : 0, // burn after reading
open_discussion ? 1 : 0 // open discussion
};
// Create the main JSON structure
// JSON close to v1.3 JSON-LD: meta.expire used by server
json paste_json = {
{"v", 2}, // version
{"adata", metadata}, // metadata
{"ct", cipher_text_b64}, // ciphertext (base64)
{"meta", {
{"expire", expiration}
}}
};
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 and decode cipher text
std::string ct_b64 = json_data.at("ct");
cipher_text = decode_base64(ct_b64);
// Optional explicit tag field
if (json_data.contains("tag")) {
std::string tag_b64 = json_data.at("tag");
auth_tag = decode_base64(tag_b64);
} else {
auth_tag.clear();
}
// Extract metadata and decode IV & salt
json adata = json_data.at("adata");
if (adata.size() >= 1) {
json first_element = adata[0];
if (first_element.is_array() && first_element.size() >= 2) {
std::string iv_b64 = first_element[0];
std::string salt_b64 = first_element[1];
iv = decode_base64(iv_b64);
salt = decode_base64(salt_b64);
}
}
// Extract expiration; servers may return either meta.expire (string)
// or meta.time_to_live (integer seconds). Both are optional for our use.
try {
expiration = json_data.at("meta").at("expire");
} catch (...) {
try {
auto ttl = json_data.at("meta").at("time_to_live").get<int>();
expiration = std::to_string(ttl);
} catch (...) {
expiration.clear();
}
}
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.value("status", 1);
if (status == 0) {
paste_id = json_response.value("id", "");
url = json_response.value("url", "");
delete_token = json_response.value("deletetoken", "");
} else {
message = json_response.value("message", "");
}
return true;
} catch (...) {
return false;
}
}