Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Makefile.in
*.l[ao]
*~
*.pc
*.a

# CMake dir
build
2 changes: 1 addition & 1 deletion src/aescrypto.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2017 Simon Désaulniers
* Copyright © 2017-2020 Simon Désaulniers
* Author: Simon Désaulniers <sim.desaulniers@gmail.com>
*
* This file is part of dpaste.
Expand Down
14 changes: 1 addition & 13 deletions src/aescrypto.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2017 Simon Désaulniers
* Copyright © 2017-2020 Simon Désaulniers
* Author: Simon Désaulniers <sim.desaulniers@gmail.com>
*
* This file is part of dpaste.
Expand Down Expand Up @@ -31,18 +31,6 @@ namespace crypto {

class AES : public Cipher {
public:
/**
* Length of the PIN if it contains the password for encrypted pasted data.
* It consists of 16 hexadecimal characters: 16*4 = 64 bits. Last 32 bits
* encode the password. Otherwise, PIN should be 32 bits.
*/
static const constexpr size_t PIN_WITH_PASS_LEN {16};
/**
* Number of bytes offsetting the password in the binary representation of
* the hexadecimal password.
*/
static const constexpr size_t CODE_PASS_OFFSET {4};

AES() {}
virtual ~AES () {}

Expand Down
220 changes: 149 additions & 71 deletions src/bin.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2017 Simon Désaulniers
* Copyright © 2017-2020 Simon Désaulniers
* Author: Simon Désaulniers <sim.desaulniers@gmail.com>
*
* This file is part of dpaste.
Expand All @@ -23,10 +23,14 @@
#include <array>
#include <iomanip>
#include <memory>
#include <algorithm>
#include <unistd.h>

#include <msgpack.hpp>

#include "bin.h"

#include "code.h"
#include "conf.h"
#include "log.h"
#include "gpgcrypto.h"
Expand All @@ -38,7 +42,7 @@ const constexpr uint8_t Bin::PROTO_VERSION;

Bin::Bin() {
/* load dpaste config */
auto config_file = conf::ConfigurationFile();
conf::ConfigurationFile config_file {};
config_file.load();
conf_ = config_file.getConfiguration();

Expand All @@ -55,32 +59,34 @@ Bin::Bin() {
std::string Bin::code_from_dpaste_uri(const std::string& uri) {
static const std::string DUP {DPASTE_URI_PREFIX};
const auto p = uri.find(DUP);
return uri.substr(p != std::string::npos ? p+DUP.length() : 0);
return uri.substr(p != std::string::npos ? p+DUP.length() : 0, code::PIN_WITH_PASS_LEN);
}

std::pair<bool, std::string> Bin::get(std::string&& code, bool no_decrypt) {
code = code_from_dpaste_uri(code);
const auto offset = crypto::AES::CODE_PASS_OFFSET*2;
const auto lcode = code.substr(0, offset);
const auto pwd = code.substr(offset);

/* first try http server */
auto data_str = http_client_->get(lcode);
std::vector<uint8_t> data {data_str.begin(), data_str.end()};

/* if fail, then perform request from local node */
if (data.empty()) {
/* get a pasted blob */
auto values = node.get(lcode);
if (not values.empty())
data = values.front();
}
std::tuple<std::string, uint32_t, std::string>
Bin::parse_code_info(const std::string& code) {
std::stringstream ns(code.substr(code::DPASTE_PIN_LEN, code::DPASTE_NPACKETS_LEN));
uint32_t npackets;
ns >> std::hex >> npackets;
return {
code.substr(0, code::DPASTE_PIN_LEN),
npackets,
code.substr(code::DPASTE_PIN_LEN+code::DPASTE_NPACKETS_LEN, code::PASSWORD_LEN)
};
}

if (not data.empty()) {
Packet p;
std::vector<uint8_t>
Bin::parse_data(const std::string& code,
std::vector<std::vector<uint8_t>>&& values,
std::string pwd,
bool no_decrypt) const
{
for (const auto& v : values) {
try {
p.deserialize(data);
Packet p;
p.deserialize(v);
std::vector<uint8_t> data;
auto cipher = crypto::Cipher::get(p.data, code);

if (cipher and not no_decrypt) {
std::shared_ptr<crypto::Parameters> params;
if (auto aes = std::dynamic_pointer_cast<crypto::AES>(cipher)) {
Expand All @@ -90,52 +96,97 @@ std::pair<bool, std::string> Bin::get(std::string&& code, bool no_decrypt) {
data = cipher->processCipherText(p.data, std::move(params));
} else
data = std::move(p.data);

if (not (cipher or p.signature.empty())) {
auto gc = std::dynamic_pointer_cast<crypto::GPG>(crypto::Cipher::get(crypto::Cipher::Scheme::GPG, {}));
DPASTE_MSG("Data is GPG signed. Verifying...");
auto res = gc->verify(p.signature, data);
if (res.numSignatures() > 0)
gc->comment_on_signature(res.signature(0));
}

return data;
} catch (const GpgME::Exception& e) {
DPASTE_MSG("%s", e.what());
return {false, ""};
return {};
} catch (const dht::crypto::DecryptError& e) {
DPASTE_MSG("%s", e.what());
return {false, ""};
return {};
} catch (msgpack::type_error& e) { } /* backward compatibility with <=0.3.3 */
}

return {};
}

std::pair<bool, std::string> Bin::get(std::string&& code, bool no_decrypt) {
code = code_from_dpaste_uri(code);
const auto parsed_code = parse_code_info(code);
const auto& lcode = std::get<0>(parsed_code);
const auto& npackets = std::get<1>(parsed_code);
const auto& pwd = std::get<2>(parsed_code);

std::vector<Packet> packets(npackets);

auto available = http_client_->isAvailable();
auto get_method = [this,&available](const auto& c) {
/* if available, try http server */
if (available) {
auto datas = http_client_->get(c);
std::vector<std::vector<uint8_t>> data;
std::transform(datas.begin(), datas.end(), std::back_inserter(data), [](const auto& s) {
std::vector<uint8_t> blob;
std::move(s.begin(), s.end(), std::back_inserter(blob));
return blob;
});
return data;
/* if fail, then perform request from local node */
} else return node.get(c);
};

std::vector<uint8_t> whole_data;
uint32_t licode;
{
std::stringstream css(lcode);
css >> std::hex >> licode;
}
for (uint8_t s = 0; s < npackets; ++s) {
std::string target = code_from_pin(licode + s);
auto values = get_method(target);
auto data = parse_data(code, std::move(values), pwd, no_decrypt);
if (data.empty())
return {false, {}};
std::move(data.begin(), data.end(), std::back_inserter(whole_data));
}
return {true, {data.begin(), data.end()}};

std::string ret;
std::move(whole_data.begin(), whole_data.end(), std::back_inserter(ret));
return {true, std::move(ret)};
}

std::vector<uint8_t> Bin::data_from_stream(std::stringstream&& input_stream) {
std::vector<uint8_t> Bin::data_from_stream(std::stringstream& input_stream, const size_t& count) {
std::vector<uint8_t> buffer;
buffer.resize(dht::MAX_VALUE_SIZE);
input_stream.read(reinterpret_cast<char*>(buffer.data()), dht::MAX_VALUE_SIZE);
buffer.resize(count);
input_stream.read(reinterpret_cast<char*>(buffer.data()), count);
buffer.resize(input_stream.gcount());
return buffer;
}

std::string Bin::random_pin() {
static std::uniform_int_distribution<uint32_t> dist;
static std::mt19937_64 rand_;
static std::random_device rdev;
static std::seed_seq seed {rdev(), rdev()};
static bool initialized = false;
if (not initialized)
rand_.seed(seed);

auto pin = dist(rand_);
std::stringstream ss;
ss << std::setfill('0') << std::setw(Bin::DPASTE_PIN_LEN) << std::hex << pin;
auto pin_s = ss.str();
std::string Bin::code_from_pin(uint32_t pin) {
auto pin_s = hexStrFromInt(pin, code::DPASTE_PIN_LEN);
std::transform(pin_s.begin(), pin_s.end(), pin_s.begin(), ::toupper);
return pin_s;
}

std::pair<Bin::Packet, std::string> Bin::prepare_data(std::vector<uint8_t>&& data, std::unique_ptr<crypto::Parameters>&& params) {
Packet p;
std::string Bin::hexStrFromInt(uint32_t i, size_t len) {
std::stringstream ss;
ss << std::setfill('0') << std::setw(len) << std::hex << i;
return ss.str();
}

std::pair<std::vector<Bin::Packet>, std::string>
Bin::prepare_data(std::stringstream&& input_stream,
std::unique_ptr<crypto::Parameters>&& params)
{
std::string pwd = "";
std::shared_ptr<crypto::Parameters> sparams(std::move(params));
std::shared_ptr<crypto::Parameters> init_params;
Expand All @@ -144,48 +195,75 @@ std::pair<Bin::Packet, std::string> Bin::prepare_data(std::vector<uint8_t>&& dat
bool to_sign {false};
if (auto gp = std::get_if<crypto::GPGParameters>(sparams.get())) {
auto& keyid = conf_.at("pgp_key_id");
to_sign = gp->sign and not keyid.empty();
scheme = gp->scheme;
to_sign = gp->sign and not keyid.empty();
scheme = gp->scheme;
init_params = std::make_shared<crypto::Parameters>();
init_params->emplace<crypto::GPGParameters>(keyid);
} else if (auto aesp = std::get_if<crypto::AESParameters>(sparams.get())) {
scheme = aesp->scheme;
pwd = random_pin();
scheme = aesp->scheme;
pwd = random_pin();
aesp->password = pwd;
}

auto cipher = crypto::Cipher::get(scheme, std::move(init_params));

if (cipher) {
auto cipher_text = cipher->processPlainText(data, std::move(sparams));
if (cipher_text.empty()) {
p.data.insert(p.data.end(), data.begin(), data.end());
if (to_sign) {
DPASTE_MSG("Signing data...");
auto res = std::dynamic_pointer_cast<crypto::GPG>(cipher)->sign(p.data);
p.signature = res.first;
}
uint32_t rsiz = 0;
const size_t SIZE_PER_PACKET = dht::MAX_VALUE_SIZE - Packet::EXTRA_SERIALIZATION_BYTES;
std::vector<Packet> packets;
packets.reserve(std::ceil(((double)Bin::DPASTE_MAX_SIZE) / SIZE_PER_PACKET));
std::vector<uint8_t> data;
while (rsiz < Bin::DPASTE_MAX_SIZE
and (data = data_from_stream(input_stream, SIZE_PER_PACKET)).size() > 0)
{
rsiz += data.size();

Packet p;
if (cipher) {
auto cipher_text = cipher->processPlainText(data, std::move(sparams));
if (cipher_text.empty()) {
p.data.insert(p.data.end(), data.begin(), data.end());
if (to_sign) {
DPASTE_MSG("Signing data...");
auto res = std::dynamic_pointer_cast<crypto::GPG>(cipher)->sign(p.data);
p.signature = std::move(res.first);
}
} else
p.data = std::move(cipher_text);
} else
p.data = cipher_text;
} else
p.data.insert(p.data.end(), data.begin(), data.end());
return {p, pwd};
p.data = std::move(data);

packets.push_back(std::move(p));
}

packets.shrink_to_fit();
return {packets, std::move(pwd)};
}

std::string Bin::paste(std::vector<uint8_t>&& data, std::unique_ptr<crypto::Parameters>&& params) {
auto code = random_pin();
std::string
Bin::paste(std::stringstream&& input_stream, std::unique_ptr<crypto::Parameters>&& params) {
auto pin = random_.integer();
auto available = http_client_->isAvailable();
auto paste_method = [this,&available](const auto& c, auto&& d) {
if (available) return http_client_->put(c, {d.begin(), d.end()});
else return node.paste(c, std::forward<std::vector<uint8_t>>(d));
};

auto pp = prepare_data(std::forward<std::vector<uint8_t>>(data), std::forward<std::unique_ptr<crypto::Parameters>>(params));
auto& p = pp.first;
auto& pwd = pp.second;
auto pp = prepare_data(std::forward<std::stringstream>(input_stream),
std::forward<std::unique_ptr<crypto::Parameters>>(params));
auto& packets = pp.first;
auto& pwd = pp.second;

DPASTE_MSG("Pasting data...");
auto bin_packet = p.serialize();
auto success = http_client_->put(code, {bin_packet.begin(), bin_packet.end()});
if (not success)
success = node.paste(code, std::move(bin_packet));
size_t shift = 0;
for (const auto& p : packets) {
auto code = code_from_pin(pin + shift++);
auto bin_packet = p.serialize();
bool success = paste_method(code, std::move(bin_packet));
if (not success)
return {};
}

return success ? DPASTE_URI_PREFIX+code+pwd : "";
return DPASTE_URI_PREFIX+code_from_pin(pin)+hexStrFromInt(shift, code::DPASTE_NPACKETS_LEN)+pwd;
}

msgpack::object*
Expand Down
Loading