Skip to content

Commit

Permalink
Merge pull request #39 from marlinprotocol/attester
Browse files Browse the repository at this point in the history
decrypting private key from keystore file
  • Loading branch information
roshanr95 committed Jan 2, 2021
2 parents f171f7e + 5765096 commit 01b1b9e
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 1 deletion.
6 changes: 6 additions & 0 deletions beacon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ target_link_libraries(beacon INTERFACE Boost::iterator)
# Boost::filesystem
target_link_libraries(beacon INTERFACE Boost::filesystem)

# cryptopp
target_link_libraries(beacon INTERFACE cryptopp::CryptoPP)

# rapidjson
target_link_libraries(beacon INTERFACE rapidjson)

install(TARGETS beacon
EXPORT marlin-beacon-export
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
Expand Down
165 changes: 164 additions & 1 deletion beacon/examples/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,44 @@
#include <marlin/asyncio/core/EventLoop.hpp>
#include <cstring>
#include <structopt/app.hpp>
#include <cryptopp/scrypt.h>
#include <cryptopp/modes.h>
#include <cryptopp/keccak.h>
#include <cryptopp/hex.h>
#include <cryptopp/aes.h>
#include <rapidjson/document.h>
#include <boost/filesystem.hpp>

using namespace marlin;

class BeaconDelegate {};

struct CliOptions {
std::optional<std::string> keystore_path;
std::optional<std::string> keystore_pass_path;
std::optional<std::string> discovery_addr;
std::optional<std::string> heartbeat_addr;
std::optional<std::string> beacon_addr;
};
STRUCTOPT(CliOptions, discovery_addr, heartbeat_addr, beacon_addr);
STRUCTOPT(CliOptions, keystore_path, keystore_pass_path, discovery_addr, heartbeat_addr, beacon_addr);

std::string get_key(std::string keystore_path, std::string keystore_pass_path);

int main(int argc, char** argv) {
try {
auto options = structopt::app("beacon").parse<CliOptions>(argc, argv);
if(options.beacon_addr.has_value()) {
if(options.keystore_path.has_value() && options.keystore_pass_path.has_value()) {
std::string key = get_key(options.keystore_path.value(), options.keystore_pass_path.value());
if (key.empty()) {
SPDLOG_ERROR("keystore file error");
return 1;
}
} else {
SPDLOG_ERROR("require keystore and password file");
return 1;
}
}
auto discovery_addr = core::SocketAddress::from_string(
options.discovery_addr.value_or("127.0.0.1:8002")
);
Expand All @@ -43,3 +66,143 @@ int main(int argc, char** argv) {

return -1;
}

std::string string_to_hex(const std::string& input)
{
std::string output;
CryptoPP::StringSource ss2( input, true,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink( output )
)); // HexEncoder
return output;
}

std::string hex_to_string(const std::string& input)
{
std::string output;
CryptoPP::StringSource ss2( input, true,
new CryptoPP::HexDecoder(
new CryptoPP::StringSink( output )
)); // HexDecoder
return output;
}

bool isvalid_cipherparams(const rapidjson::Value &cipherparams, const std::string &cipher) {
if(cipher == "aes-128-ctr") {
return cipherparams.HasMember("iv") && cipherparams["iv"].IsString();
}
return false;
}

bool isvalid_kdfparams(const rapidjson::Value &kdfparams, const std::string &kdf) {
if(kdf == "scrypt") {
return kdfparams.HasMember("dklen") && kdfparams["dklen"].IsUint64()
&& kdfparams.HasMember("n") && kdfparams["n"].IsUint64()
&& kdfparams.HasMember("p") && kdfparams["p"].IsUint64()
&& kdfparams.HasMember("r") && kdfparams["r"].IsUint64()
&& kdfparams.HasMember("salt") && kdfparams["salt"].IsString();
}
return false;
}

bool isvalid_keystore(rapidjson::Document &keystore) {
if (keystore.HasMember("crypto") && keystore["crypto"].IsObject() ) {
const rapidjson::Value &crypto = keystore["crypto"];
const rapidjson::Value &cipherparams = crypto["cipherparams"];
const rapidjson::Value &kdfparams = crypto["kdfparams"];
return crypto.HasMember("cipher") && crypto["cipher"].IsString()
&& crypto.HasMember("ciphertext") && crypto["ciphertext"].IsString()
&& crypto.HasMember("kdf") && crypto["kdf"].IsString()
&& crypto.HasMember("mac") && crypto["mac"].IsString()
&& crypto.HasMember("cipherparams") && crypto["cipherparams"].IsObject()
&& crypto.HasMember("kdfparams") && crypto["kdfparams"].IsObject()
&& isvalid_cipherparams(cipherparams, crypto["cipher"].GetString())
&& isvalid_kdfparams(kdfparams, crypto["kdf"].GetString());
}
return false;
}

void derivekey_scrypt(CryptoPP::SecByteBlock &derived, const rapidjson::Value &kdfparams, const std::string &pass) {

CryptoPP::Scrypt pbkdf;
derived = CryptoPP::SecByteBlock(kdfparams["dklen"].GetUint());
std::string salt(hex_to_string(kdfparams["salt"].GetString()));
pbkdf.DeriveKey(derived, derived.size(),
CryptoPP::ConstBytePtr(pass), CryptoPP::BytePtrSize(pass),
CryptoPP::ConstBytePtr(salt), CryptoPP::BytePtrSize(salt),
CryptoPP::word64(kdfparams["n"].GetUint64()),
CryptoPP::word64(kdfparams["r"].GetUint64()),
CryptoPP::word64(kdfparams["p"].GetUint64()));
}

std::string decrypt_aes128ctr(const rapidjson::Value &cipherparams, std::string &ciphertext, CryptoPP::SecByteBlock &derived) {
std::string iv = hex_to_string(cipherparams["iv"].GetString());

CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption d;
d.SetKeyWithIV(derived, 16, CryptoPP::ConstBytePtr(iv));

CryptoPP::SecByteBlock decrypted(ciphertext.size());
d.ProcessData(decrypted, CryptoPP::ConstBytePtr(ciphertext), CryptoPP::BytePtrSize(ciphertext));
return std::string((const char*)decrypted.data(), decrypted.size());
}

bool check_mac(const std::string &mac, CryptoPP::SecByteBlock &derived, std::string &ciphertext) {
CryptoPP::Keccak_256 hasher;
auto hashinput = CryptoPP::SecByteBlock((derived.data()+16), 16) + CryptoPP::SecByteBlock((const CryptoPP::byte*)ciphertext.data(), ciphertext.size());

CryptoPP::SecByteBlock hash(mac.size());
return hasher.VerifyTruncatedDigest((const CryptoPP::byte*)mac.data(), mac.size(), hashinput, hashinput.size());
}

std::string get_key(std::string keystore_path, std::string keystore_pass_path) {
std::string _pass;
rapidjson::Document _keystore;

// read password file
if(boost::filesystem::exists(keystore_pass_path)) {
std::ifstream fin(keystore_pass_path);
std::getline(fin, _pass);
fin.close();
}
if (_pass.empty()) {
SPDLOG_ERROR("Invalid password file");
return "";
}

// read keystore file
if(boost::filesystem::exists(keystore_path)) {
std::string s;
boost::filesystem::load_string_file(keystore_path, s);
rapidjson::StringStream ss(s.c_str());
_keystore.ParseStream(ss);
}
if (!isvalid_keystore(_keystore)){
SPDLOG_ERROR("Invalid keystore file");
return "";
}

const rapidjson::Value &crypto = _keystore["crypto"];
std::string ciphertext = hex_to_string(crypto["ciphertext"].GetString());
CryptoPP::SecByteBlock derived;
std::string decrypted;

// get derived keycrypto
if (crypto["kdf"] == "scrypt") {
derivekey_scrypt(derived, crypto["kdfparams"], _pass);
}
if (derived.size() == 0) {
SPDLOG_ERROR("kdf error");
return "";
}
if (crypto["cipher"] == "aes-128-ctr") {
decrypted = decrypt_aes128ctr(crypto["cipherparams"], ciphertext, derived);
}

//validate mac
if (!check_mac(hex_to_string(crypto["mac"].GetString()), derived, ciphertext)) {
SPDLOG_ERROR("Invalid mac");
return "";
}
SPDLOG_INFO("decrypted keystore");
return decrypted;
}

0 comments on commit 01b1b9e

Please sign in to comment.