I'm planning on using the crypto++ library for a web application and wasm seemed perfect for it.
My cpp code:
#include <string>
using std::string;
#include "cryptopp/cryptlib.h"
#include "cryptopp/rsa.h"
#include "cryptopp/files.h"
#include "cryptopp/osrng.h"
using CryptoPP::RSA;
using CryptoPP::InvertibleRSAFunction;
using CryptoPP::RSAES_OAEP_SHA_Encryptor;
using CryptoPP::RSAES_OAEP_SHA_Decryptor;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::PK_EncryptorFilter;
using CryptoPP::PK_DecryptorFilter;
using CryptoPP::FileSink;
using CryptoPP::FileSource;
using CryptoPP::AutoSeededRandomPool;
using CryptoPP::DecodingResult;
using CryptoPP::SHA1;
using namespace CryptoPP;
extern "C" {
char* generateKeys() {
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize(rng, 1024);
RSA::PrivateKey privateKey(parameters);
RSA::PublicKey publicKey(parameters);
string pubKey;
publicKey.Save(StringSink(pubKey).Ref());
privateKey.Save(FileSink("privkey.der", true).Ref());
int n = pubKey.length();
char* char_array = new char[n + 1];
strcpy(char_array, pubKey.c_str());
return char_array;
}
}
extern "C" {
char* encrypt(string pubKey, string plain) {
AutoSeededRandomPool rng;
string cipher;
RSA::PublicKey publicKey;
publicKey.Load(StringSource(pubKey, true, NULL).Ref());
RSAES_OAEP_SHA_Encryptor e(publicKey);
StringSource(plain, true,
new PK_EncryptorFilter(rng, e,
new StringSink(cipher)
) // PK_EncryptorFilter
); // StringSource
int n = cipher.length();
char* char_array = new char[n + 1];
strcpy(char_array, cipher.c_str());
return char_array;
}
}
extern "C" {
char* decrypt(const char* filename, string cipher) {
AutoSeededRandomPool rng;
RSA::PrivateKey privateKey;
string recovered;
privateKey.Load(FileSource(filename, true).Ref());
RSAES_OAEP_SHA_Decryptor d(privateKey);
StringSource(cipher, true,
new PK_DecryptorFilter(rng, d,
new StringSink(recovered)
) // PK_EncryptorFilter
); // StringSource
int n = recovered.length();
char* char_array = new char[n + 1];
strcpy(char_array, recovered.c_str());
return char_array;
}
}
I referred emscripten documentation for using libraries in emscripten
I created cryptlib.a using the cryptlib.cpp file and compiled it using emcc like so
emcc -c -o cryptlib.o cryptlib.cpp
ar rcs cryptlib.a cryptlib.o
Finally I also created Source.o like so
emcc -c -o Source.o Source.cpp
I figured out that this would the command to get an html and js file
emcc Source.o cryptlib.a -o source.html -sEXPORTED_FUNCTIONS=_generateKeys -sEXPORTED_RUNTIME_METHODS=ccall,cwrap
I get wasm-ld errors like so
Error screenshots
What am I doing wrong? I also want to expose other functions of my source code but I was testing with just one of the functions.
Looks like you try to use the native version of crypto++, and this will only work if you build the webassembly version of the library.
EDIT: sorry... you were already trying that...
I saw your question here as I was doing it myself.
this is what I just published, which might help:
=> wasm online demo:
===> https://guillaumebouchetepitech.github.io/wasm-cryptopp-demo/dist/index.html
=> source code:
===> https://github.com/GuillaumeBouchetEpitech/wasm-cryptopp-demo
PS: I've added the code to set/get string values to/from the js/wasm code as well, it's not overly well done but it feels like you could also get started on that side as well thanks to it.
let me know.
Related
I'm a noob with Crypto++, still trying to understand all the pipeline stuff etc...
The first trivial problem I have is that SavePrivateKey isn't found in the library by the compiler but when I look it up on Wiki Crypto++ the link is broken and I can't find what to #include...
The second problem is that I'd like to access the privkey and pubkey generate by the first snippet as strings in X.509/PKCS#8 format in order to manipulate them but it's not clear to me how to do that.
The last problem is that I haven't understood how to input the keys to the decrypt function as a string.
Thank you in advance if you can help me
#include <cryptopp/files.h>
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <cryptopp/sha.h>
#include "cryptopp/filters.h"
#include "cryptopp/hex.h"
#include "cryptopp/cryptlib.h"
#include <typeinfo>
int main() {
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);
RSA::PrivateKey privateKey(params);
RSA::PublicKey publicKey(params);
std::string plain="RSA Encryption", cipher, recovered;
// Encryption
RSAES_OAEP_SHA_Encryptor e(publicKey);
StringSource ss1(plain, true,new PK_EncryptorFilter(rng, e, new StringSink(cipher))); // StringSource
// Decryption
RSAES_OAEP_SHA_Decryptor d(privateKey);
StringSource ss2(cipher, true, new PK_DecryptorFilter(rng, d, new StringSink(recovered))); // StringSource
SavePrivateKey("rsa-private.key", privateKey);
SavePublicKey("rsa-public.key", publicKey);
std::cout << "Recovered plain text" << std::endl;
std::cout<<rsaPrivate<<std::endl<<rsaPublic<<std::endl;
return 0;
}
the code below implements a working example for string encryption using RSA and cryptopp. It is an adaption of the examples available from the crypto++ wiki. The problem is that an exception is thrown if the string is large than 342.
terminate called after throwing an instance of 'CryptoPP::InvalidArgument'
what(): RSA/OAEP-MGF1(SHA-1): message length of 1000 exceeds the maximum of 342 for this public key
A similar situation arises if the source/sink is a file.
There are at least 3 options to deal with that problem
make the key larger
slice the string manually before calling the encryption function
configure/call cryptopp in a way that it can handle the long string.
The latter option would be the most sensible, and I am sure it exists, but I have failed so far to figure out the correct setup.
Any ideas are highly appreciated.
#include <string>
#include <cryptlib.h>
#include <rsa.h>
#include "osrng.h"
#include <iostream>
#include <queue.h>
#include <files.h>
#include "base64.h"
using namespace std;
using namespace CryptoPP;
void b(string plain){
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);
CryptoPP::RSA::PrivateKey privateKey(params);
CryptoPP::RSA::PublicKey publicKey(params);
std::string cipher, recovered;
CryptoPP::RSAES_OAEP_SHA_Encryptor e(publicKey);
CryptoPP::StringSink sink(cipher);
CryptoPP::PK_EncryptorFilter filter(rng,e);
filter.Attach(new Redirector(sink));
CryptoPP::StringSource ss1(plain, false);
ss1.Attach(new Redirector(filter));
ss1.PumpAll();
CryptoPP::RSAES_OAEP_SHA_Decryptor d(privateKey);
CryptoPP::StringSource ss2(cipher, true,
new CryptoPP::PK_DecryptorFilter(rng, d,
new CryptoPP::StringSink(recovered)
) // PK_DecryptorFilter
); // StringSource
std::cout <<recovered << std::endl;
}
int main(){
string plain;
for(int i=0;i<1000;++i){
plain.push_back('A');
}
b(plain);
}
I installed libtesseract-dev (v4.1.1) on Ubuntu 20.04 & I am trying out a C++ code to OCR an image to searchable PDF.
My code is somewhat modified than the C++ API example code provided at official website:
/home/test/Desktop/Example2/testexample2.cpp:
#include <leptonica/allheaders.h>
#include <tesseract/baseapi.h>
#include <tesseract/renderer.h>
int main()
{
//const char* input_image = "/usr/src/tesseract-oc/testing/phototest.tif";
//const char* output_base = "my_first_tesseract_pdf";
//const char* datapath = "/Projects/OCR/tesseract/tessdata";
const char* input_image = "001.jpg";
const char* output_base = "001";
const char* datapath = ".";
int timeout_ms = 5000;
const char* retry_config = nullptr;
bool textonly = false;
int jpg_quality = 92;
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
if (api->Init(datapath, "eng")) {
fprintf(stderr, "Could not initialize tesseract.\n");
exit(1);
}
/*
tesseract::TessPDFRenderer *renderer = new tesseract::TessPDFRenderer(
output_base, api->GetDatapath(), textonly, jpg_quality);
*/
tesseract::TessPDFRenderer *renderer = new tesseract::TessPDFRenderer(
output_base, api->GetDatapath(), textonly);
bool succeed = api->ProcessPages(input_image, retry_config, timeout_ms, renderer);
if (!succeed) {
fprintf(stderr, "Error during processing.\n");
return EXIT_FAILURE;
}
api->End();
return EXIT_SUCCESS;
}
I also followed https://stackoverflow.com/a/59382664 as follows:
cd /home/test/Desktop/Example2
wget https://github.com/tesseract-ocr/tessdata/raw/master/eng.traineddata
wget https://github.com/tesseract-ocr/tesseract/blob/master/tessdata/pdf.ttf
export TESSDATA_PREFIX=$(pwd)
gedit config
(In the config file, entered the contents:
tessedit_create_pdf 1 Write .pdf output file
tessedit_create txt 1 Write .txt output file
)
g++ testexample2.cpp -o testexample2 -ltesseract
./testexample2
But on execution, it displays the errors as follows:
Warning: Invalid resolution 0 dpi. Using 70 instead.
Error during processing.
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c5241a0 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatapunc-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c506770 still has count 1 (id /home/test/Desktop/Example2/eng.traineddataword-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c9a4a70 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatanumber-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c9a4980 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatabigram-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5d7d5170 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatafreq-dawg)
My directory structure is:
Example2
|------->001.jpg
|------->config
|------->eng.traineddata
|------->pdf.ttf
|------->testexample2
|------->testexample2.cpp
I have searched about this on multiple sources, but could not find any fix for this.
Further, I would like to know if there is someway I can build a binary using C++ compilation from this code + libtesseract such that my binary becomes a standalone portable binary, running which on other Ubuntu systems would not require reinstalling tesseract libraries & their dependencies
tesseract API examples is show case for using tesseract features without covering all specifics of programming language of your choice (c++ in your case).
Just looking at your code even without trying it: you dynamically allocates memory 2x but you did not deallocate them. Try to fix these issues.
You must free use dynamic memory for your class "api"
Use:
... you code...
if (renderer) delete renderer;
if (api) delete api;
I create a file using openssl from the commandline. Let's say:
echo 'foobar' | openssl enc -aes-128-cbc -e -pass pass:testing >
sample
I can decrypt this doing
cat sample | openssl enc -aes-128-cbc -d -pass pass:testing
That works fine.
Now I want to decrypt that file using the openssl c/c++ API but I can't get this quite right. I am trying to decrypt like so
#include <fstream>
#include <memory>
#include <string>
#include <openssl/err.h>
#include <openssl/evp.h>
using EVP_CIPHER_CTX_free_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>;
int main(int argc, char* argv[])
{
ERR_load_crypto_strings();
EVP_add_cipher(EVP_aes_128_cbc());
std::string l_key{"testing"};
std::string l_ctext{};
std::ifstream l_file("sample", std::ios::binary | std::ios::in | std::ios::ate);
if(l_file.is_open())
{
std::streampos l_size = l_file.tellg();
char * lp_buffer = new char[l_size];
l_file.seekg(0, std::ios::beg);
l_file.read(lp_buffer, l_size);
l_ctext.append(lp_buffer, l_size);
delete lp_buffer;
l_file.close();
}
std::string l_rtext;
EVP_CIPHER_CTX_free_ptr ctx(EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
if(1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), NULL,
(const unsigned char*)l_key.c_str(), NULL));
ERR_print_errors_fp(stdout);
int l_len;
if(1 != EVP_DecryptUpdate(ctx.get(), (unsigned char*)&l_rtext[0], &l_len,
(const unsigned char*)l_ctext.data(),
l_ctext.size()))
ERR_print_errors_fp(stdout);
if(1 != EVP_DecryptFinal_ex(ctx.get(), (unsigned char*)&l_rtext[0] + l_len,
&l_len))
ERR_print_errors_fp(stdout);
ERR_free_strings();
exit(0);
}
However that last call to EVP_DecryptFinal_ex fails with
140559955420968:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:596:
Most of this code is based on the examples that come with the openssl API. What am I doing wrong? I set the IV to NULL in EVP_DecryptInit_ex because as far as I found out, this is acceptable as long as I don't use the key multiple times.
All this on a Fedora machine with GCC
I kind of figured out what I did wrong.
The commandline 'openssl enc' command uses a default salt, my code above didn't look for that. So (rather than try to work with the '-nosalt' option) I remove the first 16 bytes from the encrypted string and use the last 8 of that as the salt.
I was feeding the password to the EVP methods as a 'key'. It seems 'openssl enc' doesn't do this so one needs to use the salt and the password to create a key and IV (rather than using NULL which is what I did).
There was an error in the above code in that l_rtext needs a resize() to reflect the fact that characters were added to it.
The ONVIF authentication spec section 6.1.1.3 has what looks like a straight forward description of how to generate a digest. However, when using Crypto++ for Base64 and SHA1 operations, I cannot generate the same hash as the specification. I must be doing something wrong but cannot see what.
std::string nonce = "LKqI6G/AikKCQrN0zqZFlg==";
std::string dt = "2010-09-16T07:50:45Z";
std::string pwd = "userpassword";
{
// result should be tuOSpGlFlIXsozq4HFNeeGeFLEI=
// as per spec. This approach is also used here:
// https://github.com/agsh/onvif/blob/master/lib/cam.js
CryptoPP::Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
std::vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
//
CryptoPP::SHA1 hash;
byte digest[CryptoPP::SHA1::DIGESTSIZE];
hash.Update(bytes.data(), bytes.size());
hash.Update((const byte*)dt.c_str(), dt.size());
hash.Update((const byte*)pwd.c_str(), pwd.size());
hash.Final(digest);
CryptoPP::Base64Encoder encoder;
encoder.Put(digest, CryptoPP::SHA1::DIGESTSIZE);
std::string hash64(encoder.MaxRetrievable(), 0);
encoder.Get((byte*)hash64.data(), hash64.size());
// generates woEIuU+ryXxcwkTZ9ktbKGeQ
std::cout << hash64 << std::endl;
}
Any thoughts on this one much appreciated.
[edit: remove C# references]
CryptoPP::Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
std::vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
Call MessageEnd:
Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
decoder.MessageEnd();
vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
Ditto:
Base64Encoder encoder;
encoder.Put(digest, 20);
encoder.MessageEnd();
string hash64(encoder.MaxRetrievable(), 0);
encoder.Get((byte*)hash64.data(), hash64.size());
Also see Base64Encoder | Missing Data and Base64Decoder | Missing Data on the Crypto++ wiki.
Nor indeed can I duplicate the result using any other approaches, like a full C# test bed using all of the .NET crypto resources.
I don't know C# as well as Crypto++, so I can't help with a C# example that works as expected with ONVIF authentication.
Here's the result I get:
$ g++ test.cxx -I. ./libcryptopp.a -o test.exe
$ ./test.exe
tuOSpGlFlIXsozq4HFNeeGeFLEI=
And the cat test.cxx:
#include <iostream>
#include <string>
#include <vector>
#include "base64.h"
#include "sha.h"
std::string nonce = "LKqI6G/AikKCQrN0zqZFlg==";
std::string dt = "2010-09-16T07:50:45Z";
std::string pwd = "userpassword";
int main(int argc, char* argv[])
{
CryptoPP::Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
decoder.MessageEnd();
std::vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
CryptoPP::SHA1 hash;
byte digest[CryptoPP::SHA1::DIGESTSIZE];
hash.Update(bytes.data(), bytes.size());
hash.Update((const byte*)dt.c_str(), dt.size());
hash.Update((const byte*)pwd.c_str(), pwd.size());
hash.Final(digest);
CryptoPP::Base64Encoder encoder;
encoder.Put(digest, 20);
encoder.MessageEnd();
std::string hash64(encoder.MaxRetrievable(), 0);
encoder.Get((byte*)hash64.data(), hash64.size());
std::cout << hash64 << std::endl;
return 0;
}