Parse json file from AWS S3 with C++ and Jsoncpp - c++

I have this C++ function that downloads S3 files as istreams using the AWS SDK C++:
std::istream& s3read(std::string bucket, std::string key) {
Aws::Client::ClientConfiguration aws_conf;
aws_conf.region = Aws::Environment::GetEnv("AWS_REGION");
aws_conf.caFile = "/etc/pki/tls/certs/ca-bundle.crt";
Aws::S3::S3Client s3_client(aws_conf);
Aws::S3::Model::GetObjectRequest object_request;
object_request.WithBucket(bucket.c_str()).WithKey(key.c_str());
auto get_object_outcome = s3_client.GetObject(object_request);
if (get_object_outcome.IsSuccess()) {
std::istream& res = get_object_outcome.GetResult().GetBody();
return res;
} else {
...
};
};
I call it from main.cpp and try to parse it with Jsoncpp:
std::istream& stream = s3read(bucket, key);
Json::Value json;
Json::Reader reader;
reader.parse(stream, json);
However, I keep getting segmentation fault. Why?
I think that the problem is that reader.parse needs binary data and the istream isn't. But, if I'm right, how can I parse the stream as binary?

The issue you'r have is classical returning reference to temporary
You can re-design your code a little, to avoid this. For example:
static Json::Value parse_json(std::istream& src) {
Json::Value ret;
Json::Reader reader;
reader.parse(src, ret);
return ret;
}
// Aws::String is actually same thing to std::string except the allocator
// in case of Android, otherwise this is std::string as it is.
// You can use function like s3read("foo","bar");
Json::Value s3read_json(const Aws::String& bucket,const Aws::String& key) {
static constexpr const char *FILE_NAME = "/etc/pki/tls/certs/ca-bundle.crt";
Aws::Client::ClientConfiguration aws_conf;
aws_conf.region = Aws::Environment::GetEnv("AWS_REGION");
aws_conf.caFile = FILE_NAME;
Aws::S3::S3Client s3_client(aws_conf);
Aws::S3::Model::GetObjectRequest object_request;
object_request.WithBucket( bucket ).WithKey( key );
auto object_outcome = s3_client.GetObject(object_request);
if (object_outcome.IsSuccess()) {
auto result = object_outcome.GetResult();
// destructor of object_outcome is not yet called
return parse_json( result.GetBody() );
} else {
...
// throw std::runtime_error("S3 connection failed");
};
};

Related

How to compose a PKCS#7 signature file correctly?

I'm trying to use the Botan library to generate a detached signature file. The resulting signature file is not validated by OpenSSL (no other checks). Prompt in what there can be an error of formation of the signature file.
A couple of keys for signing and the certificate is stored in the HSM, it was not difficult to get them. For tests I use RSA keys and SoftHSM, later another key format and physical HSM will be used. PKCS#11 is used to communicate with HSM.
For create PKCS#7:
static const Botan::BigInt CMSVersion(1ull);
std::vector<uint8_t> createAttributes(std::vector<uint8_t> &digestData)
{
std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
Botan::OID dataOID("1.2.840.113549.1.7.1");
Botan::Attribute contentType(Botan::OIDS::str2oid("PKCS9.ContentType"),
dataOID.BER_encode());
Botan::X509_Time timeASN1(time);
std::vector<uint8_t> attributesData;
Botan::DER_Encoder attrib(attributesData);
attrib.start_cons(Botan::ASN1_Tag(0),
Botan::ASN1_Tag(Botan::ASN1_Tag::CONTEXT_SPECIFIC));
attrib.encode(contentType)
.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OID("1.2.840.113549.1.9.5"))
.start_cons(Botan::ASN1_Tag::SET).encode(timeASN1).end_cons()
.end_cons()
.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OIDS::str2oid("PKCS9.MessageDigest"))
.start_cons(Botan::ASN1_Tag::SET)
.encode(digestData, Botan::ASN1_Tag::OCTET_STRING,
Botan::ASN1_Tag::OCTET_STRING, Botan::ASN1_Tag::UNIVERSAL)
.end_cons()
.end_cons();
attrib.end_cons();
return attributesData;
}
std::vector<uint8_t> createCMS(const Botan::AlgorithmIdentifier &digestAlg,
Botan::X509_Certificate &cert,
const Botan::AlgorithmIdentifier &keyAlg,
std::vector<uint8_t> &sigData,
std::vector<uint8_t> &signedAttributes)
{
Botan::secure_vector<uint8_t> msgData;
Botan::DER_Encoder encoder(msgData);
encoder.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(CMSVersion)
.start_cons(Botan::ASN1_Tag::SET).start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(digestAlg.get_oid()).end_cons().end_cons();
Botan::OID dataOID("1.2.840.113549.1.7.1");
encoder.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(dataOID).end_cons();
encoder.start_cons(Botan::ASN1_Tag::UNIVERSAL, Botan::ASN1_Tag::PRIVATE)
.encode(cert).end_cons();
encoder.start_cons(Botan::ASN1_Tag::SET);
Botan::secure_vector<uint8_t> signerInfoData;
Botan::DER_Encoder signerInfo(signerInfoData);
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE);
signerInfo.encode(CMSVersion);
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(cert.issuer_dn())
.encode(Botan::BigInt(cert.serial_number())).end_cons();
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(digestAlg.get_oid())
.end_cons();
signerInfo.raw_bytes(signedAttributes);
signerInfo.encode(keyAlg)
.encode(sigData, Botan::ASN1_Tag::OCTET_STRING,
Botan::ASN1_Tag::OCTET_STRING, Botan::ASN1_Tag::UNIVERSAL);
signerInfo.end_cons();
encoder.raw_bytes(signerInfoData).end_cons().end_cons();
std::vector<uint8_t> resulData;
Botan::DER_Encoder result(resulData);
result.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OID("1.2.840.113549.1.7.2"))
.start_cons(Botan::ASN1_Tag::UNIVERSAL, Botan::ASN1_Tag::PRIVATE)
.raw_bytes(msgData).end_cons().end_cons();
return resulData;
}
To calculate the hash and signature using PKCS#11, as follows:
QFile input(m_content->text()), output(m_sigFile->text());
if(!input.open(QFile::ReadOnly))
{
QMessageBox::critical(this, tr("Error"),
tr("Content file '%1' not open.\n"
"Error message: %2").arg(m_content->text())
.arg(input.errorString()));
return;
}
Botan::PKCS11::PKCS11_X509_Certificate *cert = nullptr;
Botan::Private_Key *key = nullptr;
// извлечение ключа и сертификата из токена
while(!input.atEnd())
{
static const qint64 maxLen = 1024;
QByteArray data = input.read(maxLen);
(*module)->C_DigestUpdate(session->handle(),
reinterpret_cast<uchar*>(data.data()),
data.size(), &rv);
if(rv != Botan::PKCS11::ReturnValue::OK)
{
QMessageBox::critical(this, tr("Error"),
tr("Digest not run.\nError code: 0x%3")
.arg(static_cast<int>(rv), 0, 16));
delete key;
delete cert;
delete session;
delete slot;
delete module;
return;
}
}
digest.resize(102400);
ulong digestLen;
(*module)->C_DigestFinal(session->handle(), digest.data(), &digestLen, &rv);
if(rv != Botan::PKCS11::ReturnValue::OK)
{
QMessageBox::critical(this, tr("Error"),
tr("Digest not start.\nError code: 0x%3")
.arg(static_cast<int>(rv), 0, 16));
delete key;
delete cert;
delete session;
delete slot;
delete module;
return;
}
digest.resize(digestLen);
{
Botan::PKCS11::PKCS11_RNG rng(*session);
std::unique_ptr<Botan::PK_Ops::Signature> signer =
key->create_signature_op(rng,
"EMSA3(SHA-256)",
"");
signer->update(digest.data(), digest.size());
std::vector<uint8_t> attr = createAttributes(digest);
auto signData = signer->sign(rng);
for(uint8_t i : signData)
signature.push_back(i);
Botan::AlgorithmIdentifier digAlg("SHA-256", {});
auto fileData = createCMS(digAlg, *cert, key->algorithm_identifier(),
signature, attr);
output.write(reinterpret_cast<const char*>(fileData.data()),
fileData.size());
output.close();
}
When checking the received signature file, OpenSSL says
Verification failure
140365848428992:error:04091068:rsa routines:int_rsa_verify:bad signature:../crypto/rsa/rsa_sign.c:220:
140365848428992:error:2E09A09E:CMS routines:CMS_SignerInfo_verify_content:verification failure:../crypto/cms/cms_sd.c:842:
140365848428992:error:2E09D06D:CMS routines:CMS_verify:content verify error:../crypto/cms/cms_smime.c:393:

How to check if a download with Aws::Transfer::Transfermanager was a success?

I do a download from AWS S3 and at the end of the method I want to return the success of it but sadly I don't know how to.
I tried to do this with the TransferHandle I get but there does not seem to be an option for this.
bool downloadObject(const std::shared_ptr<Aws::S3::S3Client> & client, const Aws::String &key_name)
{
const auto executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>("Downloading-File", 4);
auto transferInitCallback = [&](const Aws::Transfer::TransferManager*, const std::shared_ptr<const Aws::Transfer::TransferHandle>& handle) {};
Aws::Transfer::TransferManagerConfiguration transferConfig(executor.get());
transferConfig.s3Client = client;
transferConfig.transferInitiatedCallback = transferInitCallback;
const auto transmanager = Aws::Transfer::TransferManager::Create(transferConfig);
const auto handle = transmanager->DownloadFile("bucketName", key_name, "C:/Development/test.gz");
handle->WaitUntilFinished(); // block calling thread until download complete
//if download == success -> return true;
}
You can use GetStatus to check for success, e.g.:
return handle->GetStatus() == Aws::Transfer::TransferStatus::COMPLETED

Need an example using dht_put_item with libtorrent

I'm trying to use dht to keep mutable data with libtorrent. As far as I can understand, the right way is to use the method dht_put_item from session. The problem is that I need to pass a callback function and I don't know what I'm doing wrong... my code looks like the following
namespace lt = libtorrent;
//The callback function
void cb(lt::entry& cdentry, boost::array<char,64>& cbarray, boost::uint64_t& cbint, std::string const& cbstring){
//My stuff here
}
void main(){
//The session
lt::session ses;
//The data I want to insert into DHT
std::string cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
//Now I create the keys
unsigned char seed[32];
unsigned char public_key[32];
unsigned char private_key[64];
unsigned char signature[32];
ed25519_create_seed(seed);
ed25519_create_keypair(public_key, private_key, seed);
ed25519_sign(signature, cadenaStr.c_str(), sizeof(cadenaStr.c_str()), public_key, private_key);
//How can I use this?, where is the data supposed to go? :|
ses.dht_put_item(public_key, cb, false);
}
On libtorrent/session_handler.hpp this method is defined as
void dht_put_item(boost::array<char, 32> key
, boost::function<void(entry&, boost::array<char,64>&
, boost::uint64_t&, std::string const&)> cb
, std::string salt = std::string());
Can someone please tell me what I'm doing wrong.
Thanks!
There is an example in the libtorrent repository that I use for testing. It can generate keys, put and get both mutable and immutable items.
https://github.com/arvidn/libtorrent/blob/master/tools/dht_put.cpp
How can I use this?, where is the data supposed to go? :|
You provide the data in the callback that's called. The reason for this kind of API is that there are use cases where you want to mutate the data, and then you need to first know whether something is already stored under this key, and what it is.
You are missing the settings pack for your session.
lt::settings_pack settings;
settings.set_bool(settings_pack::enable_dht, false);
settings.set_int(settings_pack::alert_mask, 0xffffffff);
ses.apply_settings(settings);
settings.set_bool(settings_pack::enable_dht, true);
ses.apply_settings(settings);
Then you need to wait until you receive a boostrap message by waiting for an alert.
wait_for_alert(ses, dht_bootstrap_alert::alert_type);
Last, your dht_put_item call:
char const* cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
dht_put_item(public_key, std::bind(&put_string, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, public_key, private_key, cadenaStr));
You will need these functions:
static alert* wait_for_alert(session* ses, int alert_type)
{
alert* ret = nullptr;
bool found = false;
while (!found)
{
ses->wait_for_alert(seconds(5));
std::vector<alert*> alerts;
ses->pop_alerts(&alerts);
for (std::vector<alert*>::iterator i = alerts.begin()
, end(alerts.end()); i != end; ++i)
{
if ((*i)->type() != alert_type)
{
continue;
}
ret = *i;
found = true;
}
}
return ret;
}
static void put_string(
entry& e
,boost::array<char, 64>& sig
,boost::int64_t& seq
,std::string const& salt
,boost::array<char, 32> const& pk
,boost::array<char, 64> const& sk
,char const* str)
{
using dht::sign_mutable_item;
if (str != NULL) {
e = std::string(str);
std::vector<char> buf;
bencode(std::back_inserter(buf), e);
dht::signature sign;
seq++;
sign = sign_mutable_item(buf, salt, dht::sequence_number(seq)
, dht::public_key(pk.data())
, dht::secret_key(sk.data()));
sig = sign.bytes;
}
}

C++ REST (Casablanca) - web::json::value has no member named 'field_map'

I am new to C++ REST ('Casablanca'). I read the tutorial here. After than, I took a sample code from there and tried to run it on my machine.
Below is the code
std::map<utility::string_t, utility::string_t> dictionary;
void handle_get(http_request request)
{
TRACE(L"\nhandle GET\n");
web::json::value::field_map answer;
for (auto const & p : dictionary)
{
answer.push_back(std::make_pair(json::value(p.first), json::value(p.second)));
}
request.reply(status_codes::OK, json::value::object(answer));
}
int main()
{
http_listener listener(L"http://127.0.0.1:8080/stockData");
listener.support(methods::GET, handle_get);
return 0;
}
In this code, I am getting error as below
I checked the header file json.h and could not find a member (class/struct) named field_map
Please help
I think below code can replace your code and should be compile latest stable version cpprestsdk v2.8.0
std::map<utility::string_t, utility::string_t> dictionary;
void handle_get(http_request request)
{
TRACE(L"\nhandle GET\n");
json::value obj;
for ( auto const & p : dictionary )
{
obj[p.first] = json::value::string(p.second);
}
// this is just for debugging
utility::stringstream_t stream;
obj.serialize(stream);
std::wcout << stream.str();
request.reply( status_codes::OK, obj);
}

winrt c++/cx concurrency access violation exception

What I'm trying to do is check for the existence of a file in the local folder and then copy it there if it isn't found (the file was previously added to the project as an asset).
Here is the code:
Windows::Storage::StorageFile^ MainPage::GetCustomFileAsync(Platform::String^ fileName)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
auto localTask = concurrency::create_task(localFolder->GetFileAsync(fileName));
StorageFile^ retVal = nullptr;
localTask.then([&](StorageFile^ t){
retVal = t;
}).then([](concurrency::task<void> t)
{
try
{
t.get();
OutputDebugString(L"Found\n");
}
catch (Platform::COMException^ e)
{
OutputDebugString(e->Message->Data());
}
}).wait();
return retVal;
}
StorageFile^ fileVar;
if ((fileVar = this->GetCustomFileAsync("somefile.txt")) == nullptr)
{
String^ path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
concurrency::create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(path)).then([](StorageFolder^ folder){
return (folder->GetFileAsync("somefile.txt"));
}).then([](StorageFile^ file){
return (file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder));
}).then([&](StorageFile^ file){
fileVar = file;
OutputDebugString(file->DisplayName->Data());
});
}
What happens is that I get an access violation exception at the point where "file" is being assigned to "fileVar" (because of cross-thread access perhaps?). How to fix this?
Edit: I can't do all the processing there because the file will be accessed many times. In short I need to know when it has been successfully copied and get a handle to it. Here is the code that works
Windows::Storage::StorageFile^ GetFile(Platform::String^ fileName)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
using Windows::Foundation::AsyncOperationCompletedHandler;
using Windows::Foundation::AsyncStatus;
using Windows::Foundation::IAsyncOperation;
using Platform::String;
auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
bool completed = false;
StorageFile^ retVal = nullptr;
localFolder->GetFileAsync(fileName)->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([&completed, &retVal, &fileName](IAsyncOperation<StorageFile^>^ fileOperation, AsyncStatus status)
{
if (status == AsyncStatus::Error)
{
String^ path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
Windows::Storage::StorageFolder::GetFolderFromPathAsync(path)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFolder^>(
[&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFolder^>^ folderOperation, AsyncStatus status)->void{
auto assetFolder = folderOperation->GetResults();
assetFolder->GetFileAsync(fileName)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>([&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFile^>^ fileOperation, AsyncStatus status)->void{
auto file = fileOperation->GetResults();
file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>
([&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFile^>^ fileOperation, AsyncStatus status)->void {
retVal = fileOperation->GetResults();
completed = true;
});
});
});
}
else
{
retVal = fileOperation->GetResults();
completed = true;
}
});
while (completed == false);
return retVal;
}
Rather than passing a delegate as an argument and returning void, make your method return task<StorageFile^> and then the caller can do a .then() to continue working once the operation has succeeded.
Or if this is exposed as a public WinRT method (not an internal / private C++ method) then use IAsyncOperation<StorageFile^>^ as the return type, and wrap the whole thing in create_async():
IAsyncOperation<StorageFile^>^ DoStuff(params)
{
return concurrency::create_async([params]
{
// function body goes here
});
}
Here's a solution I put together. Two things that are important to know:
When executing an asynchronous operation using concurrency::create_task the async operation(s) can still be executing when the parent function returns. So the captured variables MUST outlive the context of the parent function. Which obviously won't happen if they are being passed by reference. It took a while to realize this.
WinRT imposes certain restrictions on the concurrency runtime. Calling concurrency::task::get() or concurrency::task::wait() will throw an exception in an STA thread, unless the call is in a task continuation.
More information in this post:
http://social.msdn.microsoft.com/Forums/windowsapps/en-US/ae54980b-41ce-4337-a059-2213b549be4b/concurrencyinvalidoperation-when-calling-tasktget?forum=winappswithnativecode
In that case how to know when the function has finished doing it's job? I opted to pass in a callback (AKA delegate).
delegate void FileOperation(Windows::Storage::StorageFile^ file);
void GetFileConcurrency(Platform::String^ fileName, FileOperation^ fileOp)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
using Platform::String;
auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
String^ assetFolderPath = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
auto localFolderTask = concurrency::create_task(localFolder->GetFileAsync(fileName));
localFolderTask.then([localFolder, assetFolderPath, fileName, fileOp](concurrency::task<StorageFile^> theTask){
try
{
StorageFile^ theFile = theTask.get();
fileOp(theFile);
}
catch (Platform::Exception^ e)
{
OutputDebugString(e->Message->Data());
auto assetFolderTask = concurrency::create_task(StorageFolder::GetFolderFromPathAsync(assetFolderPath));
assetFolderTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFolder^ assetFolder){
auto assetFileTask = concurrency::create_task(assetFolder->GetFileAsync(fileName));
assetFileTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFile^ file){
auto copyFileTask = concurrency::create_task(file->CopyAsync(localFolder));
copyFileTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFile^ file){
OutputDebugString(file->Path->Data());
fileOp(file);
});
});
});
}
});
}