How to change sink in Crypto++ - c++

I'm using Crypto++ to decrypt a file, so I use FileSource as my source but I want to be able to change sink, so I can achieve something like following:
std::string temp;
FileSource file("/path/to/file", false, new StringSink(temp));
file.Pump(14);
if (temp != "File Signature")
return false;
//change file's sink to new CTR_Mode<AES>::Decryption(meta_key, 32, meta_iv, new StringSink(metainfo))
file.Pump(256);
/* use metainfo */
//change file's sink to new CTR_Mode<AES>::Decryption(key, 32, iv, new StringSink(decoded))
while(!file.SourceExhausted())
{
file.Pump(512);
std::cout << decoded;
}
How can I achieve this?

How to change sink in Crypto++?
A Sink is just a Filter that has no attached transformation. To change a sink, you just change the attached filter of the predecessor or parent object. The tricky part is getting access to a filter that's two or three deep in the filter chain.
Use something like the following. Filters have two methods for attaching filters: Attach and Detach. They both attach a new filter to the object; but Attach returns the old filter while Detach free's it.
The other oddity is the Redirector. You can use it to break ownership in a chain. Its kind of needed for the StreamTransformationFilter filter. The stack based allocation will be free'd as a local variable, so you don't want it free'd as part of a chain, too.
FileSource file("/path/to/file", false, new StringSink(temp));
file.Pump(14);
if (temp != "File Signature")
return false;
CTR_Mode<AES>::Decryption decryptor;
StreamTransformationFilter filter(decryptor);
// Detach StringSink(temp), Attach StreamTransformationFilter(decryptor)
file.Detach(new Redirector(filter));
// Set Key and IV
decryptor.SetKeyWithIV(meta_key, 32, meta_iv);
// Detach nothing, Attach StringSink(metainfo)
filter.Detach(new StringSink(metainfo));
// FileSource → decryptor → metainfo
file.Pump(256);
// Set Key and IV
decryptor.SetKeyWithIV(key, 32, iv);
// Detach StringSink(metainfo), Attach StringSink(decoded)
filter.Detach(new StringSink(decoded));
while(!file.SourceExhausted())
{
// FileSource → decryptor → decoded
file.Pump(512);
std::cout << decoded;
}
Here's another way to do it without the Redirector. It stashes away a pointer to the StreamTransformationFilter:
FileSource file("/path/to/file", false, new StringSink(temp));
file.Pump(14);
if (temp != "File Signature")
return false;
CTR_Mode<AES>::Decryption decryptor;
StreamTransformationFilter* filter = NULL;
// Detach StringSink(temp), Attach StreamTransformationFilter(decryptor)
file.Detach(filter = new StreamTransformationFilter(decryptor));
// Set Key and IV
decryptor.SetKeyWithIV(meta_key, 32, meta_iv);
// Detach nothing, Attach StringSink(metainfo)
filter->Detach(new StringSink(metainfo));
// FileSource → decryptor → metainfo
file.Pump(256);
// Set Key and IV
decryptor.SetKeyWithIV(key, 32, iv);
// Detach StringSink(metainfo), Attach StringSink(decoded)
filter->Detach(new StringSink(decoded));
while(!file.SourceExhausted())
{
// FileSource → decryptor → decoded
file.Pump(512);
std::cout << decoded;
}
You might be interested in Pipelining on the Crypto++ wiki. Also of interest might be BufferedTransformation, which is the base class used for pipelining.

Related

problem with crypto++ ECIES BERDecodePrivateKey

I'm trying to Store ECIES num0 PrivateKey with DEREncodePrivateKey to a std::string and reload it in num1 PrivateKey Object for testing.
Problem is when key is loaded with BERDecodePrivateKey in second PrivateKey object it can't be validated (also tested encryption and decrypting without validation and didn't decrypt )
here's the code
using namespace CryptoPP;
CryptoPP::AutoSeededRandomPool prng;
ECIES<ECP>::PrivateKey pp;
pp.Initialize(prng, ASN1::secp256k1());
/* returns true*/
bool val=pp.Validate(prng, 3);
std::string saves;
StringSink savesink(saves);
pp.DEREncodePrivateKey(savesink);
/*additional unnecessary steps to make sure the key is written completely */
savesink.MessageEnd();
savesink.Flush(true);
ECIES<ECP>::PrivateKey pro;
StringSource savesSource(saves, true);
pro.BERDecodePrivateKey(savesSource,true,savesSource.MaxRetrievable());
/*here the exception is thrown */
pro.ThrowIfInvalid(prng, 3);
finally found what the problem is
as #maarten-bodewes mentioned in comment the DER encoded private exponent doesn't determine the curve OID for the privateKey Object , so before BER Decoding and importing key we need to somehow determine the OID for the Object;
the simplest way is to determine it when Initializing new Object
above code changes to :
ECIES<ECP>::PrivateKey pro;
StringSource savesSource(saves, true);
auto rett = savesSource.MaxRetrievable();
pro.Initialize(prng, ASN1::secp256k1());
pro.BERDecodePrivateKey(savesSource,true,savesSource.MaxRetrievable());
also you AccessGroupParameters().Initialize(/*OID*/); or Initialize(/*OID*/) for existing object

Import a persistent key in to Windows key storage using CNG storage functions

I'm trying to import a persistent RSA public key into the key storage. I read on the CNG help page that it's possible for private keys and I wonder if I can also apply is to public keys (specifically the BCRYPT_RSAPUBLIC_BLOB). I've tried with the following code, but in the import section, when I call NCryptSetProperty to set the public blob as a property, I get "Error 0x80090029" which is NTE Bad Data. Having trouble debugging why this function is failing.
NCRYPT_PROV_HANDLE providerHandle = NULL;
NCRYPT_KEY_HANDLE keyHandle = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
PBYTE blob = NULL;
DWORD blob_len = 0;
///////////////////Export Test (extract key from storage)///////////////////////////
// Open handle to the Key Storage Provider
if(FAILED(status = NCryptOpenStorageProvider(
&providerHandle, //OUT: provider handle
MS_KEY_STORAGE_PROVIDER, //IN: Microsoft key storage provider
0))) //IN: dwFlags (unused)
{
//report fail
}
// Open key in the Key Storage Provider
if (FAILED(status = NCryptOpenKey(
providerHandle,
&keyHandle,
keyName.c_str(),
0,
0)))
{
//report fail
}
// (2 step key extraction process) 1. Get size of key
if (FAILED(status = NCryptExportKey(
keyHandle, //IN: Handle of the key to export
NULL, //IN(opt): key used to encrypt exported BLOB data <-- potentially an safer way for key extraction, encrypt it with a key during extraction (decrypt with NCryptDecrypt)
BCRYPT_RSAPUBLIC_BLOB, //IN: BLOB type (https://msdn.microsoft.com/en-us/library/windows/desktop/aa376263%28v=vs.85%29.aspx)
NULL, //IN(opt): List of paramters for the key
NULL, //OUT(opt): Output byte buffer
0, //IN: Size of the output buffer
&blob_len, //OUT: Amount of bytes copied to the output buffer
0))) //IN: Flag to modify function behaviour (0 means no flag set)
{
//report fail
}
// Allocate data blob to store key in
blob = (PBYTE)malloc(blob_len);
if (NULL == blob) {
//report fail
}
// (2 step key extraction process) 2. Get key and store in byte array (Extracted key is in form of BCRYPT_RSAKEY_BLOB)
if (FAILED(status = NCryptExportKey(
keyHandle,
NULL,
BCRYPT_RSAPUBLIC_BLOB,
NULL,
blob,
blob_len,
&blob_len,
0)))
{
//report fail
}
///////////////Import Test (Store into storage)//////////////////////////////////////////////
// Create a persisted key
if(FAILED(status = NCryptCreatePersistedKey(
providerHandle, //IN: provider handle
&keyHandle, //OUT: Handle to key
NCRYPT_RSA_ALGORITHM, //IN: CNG Algorithm Identifiers. NCRYPT_RSA_ALGORITHM creates public key
keyName.c_str(), //IN: Key name. If NULL, the key does not persist
0, //IN: Key type
NCRYPT_OVERWRITE_KEY_FLAG)))//IN: Behaviour: 0 - apply to current user only, NCRYPT_MACHINE_KEY_FLAG - apply to local comp only, NCRYPT_OVERWRITE_KEY_FLAG - overwrite existing key
{
//report fail
}
// Set the size of the key
if(FAILED(status = NCryptSetProperty(
keyHandle, //IN: Handle to key
BCRYPT_RSAPUBLIC_BLOB, //IN: CNG Algorithm Identifiers. BCRYPT_RSAPUBLIC_BLOB allows me to use set this blob as the new key's blob
blob, //IN: Key name. If NULL, the key does not persist
blob_len, //IN: Key Length
0))) //IN: Bahaviour: 0 - apply to current user only, NCRYPT_MACHINE_KEY_FLAG - apply to local comp only, NCRYPT_OVERWRITE_KEY_FLAG - overwrite existing key
{
//report fail <<-------------------------- Fail here
}
// Finalize key generation (Key is now usable, but uneditable)
if(FAILED(status = NCryptFinalizeKey(keyHandle, 0))) {
//report fail
}
////////////////////////////////////////////////////////////////////////
On creation of an asymmetric key, one of the properties that can be set is the NCRYPT_EXPORT_POLICY_PROPERTY. I used this to control whether the private could be read or not.
//... after NCryptCreatePersistedKey()
DWORD export_policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
if(FAILED(status = NCryptSetProperty(
keyHandle,
NCRYPT_EXPORT_POLICY_PROPERTY,
(PBYTE)&export_policy,
static_cast<DWORD>(sizeof(DWORD)),
NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG)))
{
//report error
}
//... before NCryptFinalizeKey()
The properties are defined here.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa376242(v=vs.85).aspx

Insert raw file data into BLOB ("OLE Object") field of Access table using ADO

I am trying to insert a file into MS Access database, into a field of OLE Object type. I am using C++ and ADO.
Currently I get error Invalid pointer error.
I think that my problem is mishandling variants since this is the first time I use them. I am learning from this code example but have problem understanding how to insert file from disk into variant.
They read it from database, and copied it into new record so the part where I read file from disk and then insert it into variant is missing.
I am firing off my code in GUI when menu item is selected. Database has one table named test with fields ID which is primary key and field which is of OLE Object type.
After searching online I have found nothing that can help me.
Here is smallest code snippet possible that illustrates the problem ( error checking is minimal):
wchar_t *bstrConnect = L"Provider=Microsoft.ACE.OLEDB.12.0; \
Data Source = C:\\Users\\Smiljkovic85\\Desktop\\OLE.accdb";
try
{
HRESULT hr = CoInitialize(NULL);
// connection
ADODB::_ConnectionPtr pConn(L"ADODB.Connection");
// recordset
ADODB::_RecordsetPtr pRS(L"ADODB.Recordset");
// connect to DB
hr = pConn->Open(bstrConnect, L"admin", L"", ADODB::adConnectUnspecified);
// open file
std::ifstream in(L"C:\\Users\\Smiljkovic85\\Desktop\\file.pdf",
std::ios::ate | std::ios::binary);
// get file size
int fileSize = in.tellg();
// here I tried to adapt code from the example linked above
pRS->Open(L"test", _variant_t((IDispatch*)pConn, true),
ADODB::adOpenKeyset, ADODB::adLockOptimistic, ADODB::adCmdTable);
// add new record
pRS->AddNew();
// copy pasted
_variant_t varChunk;
SAFEARRAY FAR *psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
// modify to our file size
rgsabound[0].cElements = fileSize;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
//=================== try to add file into variant
char *chData = (char *)psa->pvData;
chData = new char[fileSize];
in.read(chData, fileSize);
/* ============= I have even tried the following :
char *chData = new char[fileSize];
in.read(chData, fileSize);
BYTE* pData;
SafeArrayAccessData(psa, (void **)&pData);
memcpy(pData, chData, fileSize);
SafeArrayUnaccessData(psa);
===============*/
//=================================================
// Assign the Safe array to a variant.
varChunk.vt = VT_ARRAY | VT_UI1;
varChunk.parray = psa;
pRS->Fields->GetItem(L"field")->AppendChunk(varChunk);
// add this record into DB
pRS->Update();
// cleanup
delete[] chData;
in.close();
pRS->Close();
pConn->Close();
CoUninitialize();
}
catch (_com_error e)
{
MessageBox(hWnd, (LPWSTR)e.Description(), L"", 0);
}
Can you help me to modify this code snippet so I can insert file into variant?
EDIT:
I have searched here for help and two posts that gave me an idea. Still none of my solutions work. You can see them in the above code snippet, in the comments.
What I get now, is the following error: a problem occurred while microsoft access was communicating with the ole server or activex control in MS Access. I have searched online for solution but had no luck, every link claims it has to do with access and not with the code.
Please help...
Since you are already using ADODB.Connection and ADODB.Recordset objects you should be able to use a binary ADODB.Stream object to manipulate the file contents with
.LoadFromFile to fill the Stream with the file contents, and
.Read to pull it back out of the Stream and store it in the database field.
Unfortunately I cannot offer a C++ example, but in VBA the code would be:
Dim con As ADODB.Connection, rst As ADODB.Recordset, strm As ADODB.Stream
Set con = New ADODB.Connection
con.Open _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Users\Public\Database1.accdb"
Set rst = New ADODB.Recordset
rst.Open "test", con, adOpenKeyset, adLockOptimistic, adCmdTable
Set strm = New ADODB.Stream
strm.Type = adTypeBinary
strm.Open
strm.LoadFromFile "C:\Users\Gord\Desktop\test.pdf"
rst.AddNew
strm.Position = 0
rst.Fields("FileData").Value = strm.Read
rst.Update
rst.Close
Set rst = Nothing
con.Close
Set con = Nothing
strm.Close
Set strm = Nothing

Crash when trying to encrypt a file with Crypto++ RSA Scheme

I have successfully used this some lines ago in my program:
string tmp;
StringSource(msg, true, new PK_EncryptorFilter(*rng, *encryptor, new CryptoPP::HexEncoder(new StringSink(tmp))));
return tmp;
So you know that the Crypto++ objects are well created and so.
Now I want to encrypt a whole binary file and save it to an adjacent file:
FileSource(file.c_str(), true, new PK_EncryptorFilter(*rng, *encryptor, new FileSink((file+".xx").c_str(), true)),true);
But this last line crashes with a debug error stating that abort() has been called.
Hunting down the error, I tried to change the second argument to the FileSource call to false, leading to the following code:
FileSource(file.c_str(), false, new PK_EncryptorFilter(*rng, *encryptor, new FileSink((file+".xx").c_str(), true)),true);
And then the error gone, but the destination file weights 0 bytes, nothing was read/wrote.
I do not know what can can the key to the problem, so, I hope someone can help a little bit.
EDIT: I am using Visual Studio 2013 Pro.
EDIT2: I hunted the error further.
This works and the file binary content is correctly printed on screen:
string s;
FileSource file2("C:\\test.jpg", true, new StringSink(s));
std::cout << s << std::endl;
But this don't work and ends with the mentioned crash.
string s;
FileSource file2("C:\\test.jpg", true, new PK_EncryptorFilter(*rng, *encryptor, new StringSink (s)));
std::cout << s << std::endl;
This is so strange since the same PK_EncryptorFilter filter is used in another method without trouble, as I stated at the beginning of the post.
Anyway, I post here my entire class, so as to get a clear idea of what is going on:
RSASystem::RSASystem()
{
std::string pubkey = "...OMITED...";
rng = new AutoSeededRandomPool;
CryptoPP::HexDecoder decoder;
decoder.Put((byte*)pubkey.c_str(), pubkey.size());
decoder.MessageEnd();
CryptoPP::HexDecoder decoder2;
decoder2.Put((byte*)pubkey.c_str(), pubkey.size());
decoder2.MessageEnd();
verifier = new RSASSA_PKCS1v15_SHA_Verifier;
encryptor = new RSAES_OAEP_SHA_Encryptor;
verifier->AccessKey().Load(decoder);
encryptor->AccessKey().Load(decoder2);
}
string RSASystem::encrypt(string msg)
{
string tmp;
StringSource(msg, true, new PK_EncryptorFilter(*rng, *encryptor, new CryptoPP::HexEncoder(new StringSink(tmp))));
return tmp;
}
void RSASystem::encryptFile(string file)
{
FileSource(file.c_str(), true, new PK_EncryptorFilter(*rng, *encryptor, new FileSink((file+".xx").c_str(), true)),true);
}
EDIT 3: After surrounding the code with try..catch() I got this error:
RSA/OAEP-MGF1(SHA-1): message length of 490986 exceeds the maximum of 214 for this public key
Which now I think can be easily solved.
FileSource(file.c_str(), false,
new PK_EncryptorFilter(*rng, *encryptor,
new FileSink((file+".xx").c_str(), true)
),
true);
This does not look right. new FileSink((file+".xx").c_str() returns a char*, and you need a pointer to a Sink. Plus, there's an extra false in there I'm not used to seeing. Something like:
FileSource fs1(filename, true,
new PK_EncryptorFilter(rng, encryptor,
new FileSink(filename, true)
) // PK_EncryptorFilter
); // StringSource
There's a couple of examples on the Crypto++ wiki. See RSA Cryptography and RSA Encryption Schemes.
The following is an example from the Crypto++ wiki using RSA. But you can use the code for any cryptosystem that adheres to PK_Encryptor and PK_Decryptor (Sources (like StringSource and FileSource) and Sinks (like StringSink and FileSink) are also interchangeable):
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize( rng, 1536 );
RSA::PrivateKey privateKey( params );
RSA::PublicKey publicKey( params );
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 )
) // PK_EncryptorFilter
); // StringSource
////////////////////////////////////////////////
// Decryption
RSAES_OAEP_SHA_Decryptor d( privateKey );
StringSource ss2( cipher, true,
new PK_DecryptorFilter( rng, d,
new StringSink( recovered )
) // PK_DecryptorFilter
); // StringSource
assert( plain == recovered );
Also, don't use anonymous declarations. Some versions of GCC has problems with them. That is, use:
StringSource ss1( plain, true,
...
rather than:
StringSource( plain, true,
...
I had already pending the encryption and security subject so I wasn't aware of the limitation on the length of the message of the RSA scheme.
https://security.stackexchange.com/questions/44702/whats-the-limit-on-the-size-of-the-data-that-public-key-cryptos-can-handle
So the solution passes by implementing an Integrated or Hybrid Encryption Scheme, like ECIES.
I've done this successfully with Crypto++ using: http://www.cryptopp.com/wiki/Elliptic_Curve_Integrated_Encryption_Scheme
Thanks to jww to point to the correct decision.
OK, I think I know where you might be having problems. But I'd need to see all your code and not just the encryption.
I could coax a BER Decode error by omitting encoder1.MessageEnd and encoder2.MessageEnd. Apparently, I was able to read the key before it was fully written. I assume it was fully written after leaving main (and the destructors ran) because the file sizes looked OK with ls.
In the code below, the message was encrypted under publicKey1 and then decrypted with privateKey2 to ensure the keys were round-tripping.
try {
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 1024);
RSA::PrivateKey privateKey1(params);
RSA::PublicKey publicKey1(privateKey1);
////////////////////////////////////////////////
// Save/Load keys
HexEncoder encoder1(new FileSink("private-key-der.txt", true));
HexEncoder encoder2(new FileSink("public-key-der.txt", true));
privateKey1.Save(encoder1);
publicKey1.Save(encoder2);
// Must have these. Otherwise, the full key (hex encoded)
// is not written until destructors are run
encoder1.MessageEnd();
encoder2.MessageEnd();
FileSource fs1("private-key-der.txt", true, new HexDecoder);
FileSource fs2("public-key-der.txt", true, new HexDecoder);
RSA::PrivateKey privateKey2;
RSA::PublicKey publicKey2;
privateKey2.Load(fs1);
bool valid = privateKey2.Validate(rng, 3);
if(!valid)
throw Exception(Exception::OTHER_ERROR, "Failed to validate key 1");
publicKey2.Load(fs2);
valid = publicKey2.Validate(rng, 3);
if(!valid)
throw Exception(Exception::OTHER_ERROR, "Failed to validate key 2");
////////////////////////////////////////////////
// Scratch
string plain="RSA Encryption", cipher, recovered;
////////////////////////////////////////////////
// Encryption
RSAES_OAEP_SHA_Encryptor encryptor(publicKey1);
StringSource ss1(plain, true,
new PK_EncryptorFilter(rng, encryptor,
new StringSink(cipher)
) // PK_EncryptorFilter
); // StringSource
////////////////////////////////////////////////
// Decryption
RSAES_OAEP_SHA_Decryptor decryptor(privateKey2);
StringSource ss2(cipher, true,
new PK_DecryptorFilter(rng, decryptor,
new StringSink(recovered)
) // PK_DecryptorFilter
); // StringSource
cout << "Recovered plain text: " << recovered << endl;
} catch (const Exception& ex) {
cerr << ex.what() << endl;
}

Get ECDSA signature with Crypto++

I have to get ECDSA signature in variable using Crypto++.
I tried to get it after launching SignMessage but signature is empty.
How could i get it?
Have you had a look at the Crypto++ wiki? There's a lot of stuff on Elliptic Curve Digital Signature Algorithm.
Its not really clear what you are doing or where things went wrong, so here's a copy and paste from the wiki:
Signing:
ECDSA<ECP, SHA1>::PrivateKey privateKey;
privateKey.Load(...);
AutoSeededRandomPool prng;
string message = "Yoda said, Do or do not. There is no try.";
string signature;
StringSource ss1( message, true /*pump all*/,
new SignerFilter( prng,
ECDSA<ECP,SHA1>::Signer( privateKey ),
new StringSink( signature )
) // SignerFilter
); // StringSource
Verification:
ECDSA<ECP, SHA1>::PublicKey publicKey;
publicKey.Load(...);
// Result of the verification process
bool result = false;
// Exactly what was signed in the previous step
string message = ...;
// Output from the signing operation in the previous step
string signature = ...;
StringSource ss2( signature+message, true /*pump all*/,
new SignatureVerificationFilter(
ECDSA<ECP,SHA1>::Verifier(publicKey),
new ArraySink( (byte*)&result, sizeof(result) )
) // SignatureVerificationFilter
);
// Verification failure?
if( !result ) {...}
If you would like the verifcation to throw on a failure, then try:
static const int VERIFICATION_FLAGS = SIGNATURE_AT_BEGIN | THROW_EXCEPTION;
StringSource ss3( signature+message, true /*pump all*/,
new SignatureVerificationFilter(
ECDSA<ECP,SHA1>::Verifier(publicKey),
NULL, /* No need for attached filter */
VERIFICATION_FLAGS
) // SignatureVerificationFilter
);