I wrote the encryption function using Crypto++ library, function behaves correctly when a file encryption is done for the first time. If the same encrypted file is passed again for encryption, generates the output which includes encrypted and decrypted data.
bool EncryptDataFile(const char* inputFile, const char* outputFile)
{
try
{
std::vector<byte> key = HexDecoding(PASSCODE);
std::vector<byte> iv = HexDecoding(INITIALIZATION_VECTOR);
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
FileSource fs(inputFile, true,
new AuthenticatedEncryptionFilter(encryptor,
new FileSink(outputFile), false, TAG_SIZE));
}
catch(...)
{
return false;
}
return true;
}
Input.txt:
Privacy and Security
Output1.txt - first time encryption output:
{)ªei ?ñìCzN[hç&Ää€|Ùrñ½…
Ä
Input "Output1.txt", Output "Output2.txt" - second time encryption:
Privacy and Security]®Ÿwþñ úeS„£Fpä40WL ,ÈR¯M
It has revealed the original data. An not sure what is missing here.
If the same encrypted file is passed again for encryption, generates the output which includes encrypted and decrypted data.
If I am parsing things correctly, you are saying m ≅ Enc(Enc(m)) instead of c = Enc(Enc(m)) in your encryption scheme. This is one of the reasons why you should avoid designing your own scheme.
This can happen in several scenarios, like with a stream cipher or block cipher in counter mode when re-using a key and iv.
You should using a different security context for each message or encryption operation. With some hand waiving, that means change the key or iv for each message or encryption operation.
std::vector<byte> key = HexDecoding(PASSCODE);
std::vector<byte> iv = HexDecoding(INITIALIZATION_VECTOR);
This is likely your problem. You need to use a different security context for each message or encryption operation.
Here is how you fix it. You use a key derivation function to derive different security parameters for each encryption. In the code below, the 32-byte key is divided into two 16-byte keys. The same applies to the iv. The first encryption uses key+0 and iv+0; and the second encryption uses key+16 and iv+16.
cryptopp$ cat test.cxx
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "aes.h"
#include "gcm.h"
#include "hex.h"
#include "hkdf.h"
#include "sha.h"
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
std::string password = "super secret password";
SecByteBlock key(32), iv(32);
HKDF<SHA256> hkdf;
hkdf.DeriveKey(key, key.size(),
(const byte*)password.data(), password.size(),
NULL, 0, // salt
(const byte*)"key derivation", 14);
hkdf.DeriveKey(iv, iv.size(),
(const byte*)password.data(), password.size(),
NULL, 0, // salt
(const byte*)"iv derivation", 13);
std::string m = "Yoda said, Do or do not. There is no try.";
std::string c1, c2;
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, 16, iv, 16);
StringSource(m, true, new AuthenticatedEncryptionFilter(
encryptor, new StringSink(c1)));
encryptor.SetKeyWithIV(key+16, 16, iv+16, 16);
StringSource(c1, true, new AuthenticatedEncryptionFilter(
encryptor, new StringSink(c2)));
std::cout << "Hex(m):" << std::endl;
StringSource(m, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
std::cout << "Hex(Enc(m)):" << std::endl;
StringSource(c1, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
std::cout << "Hex(Enc(Enc(m))):" << std::endl;
StringSource(c2, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
return 0;
}
Here is a run of the program:
cryptopp$ ./test.exe
Hex(m):
596F646120736169642C20446F206F7220646F206E6F742E205468657265206973206E6F20747279
2E
Hex(Enc(m)):
D4A9063DE7400E90627DE90D16346DC5A99740C55F6FEE092A99071F55F1BDB25A72B7422126CCC4
09B5B5C0076E39EBF7256D5DC3151A738D
Hex(Enc(Enc(m))):
83A459F2D4A1627624AF162590465AC705C8AC0F4D915E4A4A9D300156C5F9E042CAA47903353F0A
A1FAE408D5747DD223AC4F9AEF3C320EEF7E79E08AB2C6FBEAE7A3A5B4978C45C7
I think your scheme has some additional problems. For example, if you encrypt the message "Attack at dawn!" multiple times, then you get the same ciphertext on each run. It is leaking information, and it lacks ciphertext indistinguishability.
I think you should avoid your scheme, and use an Elliptic Curve Integrated Encryption Scheme (ECIES). It avoids most of the latent problems in your scheme, and achieves IND-CCA2.
The downside to ECIES is, you have to manage a public/private keypair. It is not a big downside, though. You are already managing a password and iv, so changing from a password to a private key is not much more work.
Related
I've been working on a project recently that should connect to a server with the help of a protocol. So far so good, but when I combed to decrypt the packages, I quickly noticed that something is not working properly.
The first 16 bytes of all packets are decrypted incorrectly. I have tried it with different libraries but that does not work either. I work in the C++ language and have so far used Crypto++ and OpenSSL for decryption, without success.
Under this Link you can find the protocol, here the decryption protocol Link and here is my corresponding code:
OpenSSL:
void init() {
unsigned char* sharedSecret = new unsigned char[AES_BLOCK_SIZE];
std::generate(sharedSecret,
sharedSecret + AES_BLOCK_SIZE,
std::bind(&RandomGenerator::GetInt, &m_RNG, 0, 255));
for (int i = 0; i < 16; i++) {
sharedSecretKey += sharedSecret[i];
}
// Initialize AES encryption and decryption
if (!(m_EncryptCTX = EVP_CIPHER_CTX_new()))
std::cout << "123" << std::endl;
if (!(EVP_EncryptInit_ex(m_EncryptCTX, EVP_aes_128_cfb8(), nullptr, (unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str())))
std::cout << "123" << std::endl;
if (!(m_DecryptCTX = EVP_CIPHER_CTX_new()))
std::cout << "123" << std::endl;
if (!(EVP_DecryptInit_ex(m_DecryptCTX, EVP_aes_128_cfb8(), nullptr, (unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str())))
std::cout << "123" << std::endl;
m_BlockSize = EVP_CIPHER_block_size(EVP_aes_128_cfb8());
}
std::string result;
int size = 0;
result.resize(1000);
EVP_DecryptUpdate(m_DecryptCTX, &((unsigned char*)result.c_str())[0], &size, &sendString[0], data.size());
Crypto++:
CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption AESDecryptor((byte*)sharedSecret.c_str(), (unsigned int)16, sharedSecret.c_str(), 1);
std::string sTarget("");
CryptoPP::StringSource ss(data, true, new CryptoPP::StreamTransformationFilter(AESDecryptor, new CryptoPP::StringSink(sTarget)));
I think important to mention is that I use one and the same shared secret for the key and the iv (initialization vector). In other posts, this was often labeled as a problem. I do not know how to fix it in this case because the protocol want it.
I would be looking forward to a constructive feedback.
EVP_EncryptInit_ex(m_EncryptCTX, EVP_aes_128_cfb8(), nullptr,
(unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str()))
And:
CFB_Mode<AES>::Decryption AESDecryptor((byte*)sharedSecret.c_str(),
(unsigned int)16, sharedSecret.c_str(), 1);
std::string sTarget("");
StringSource ss(data, true, new StreamTransformationFilter(AESDecryptor, new StringSink(sTarget)));
It is not readily apparent, but you need to set feedback size for the mode of operation of the block cipher in Crypto++. The Crypto++ feedback size is 128 by default.
The code to set the feedback size of CFB mode can be found at CFB Mode on the Crypto++ wiki. You want the 3rd or 4th example down the page.
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(iv));
That is kind of an awkward way to pass parameters. It is documented in the sources files and on the wiki at NameValuePairs. It allows you to pass arbitrary parameters through consistent interfaces. It is powerful once you acquire a taste for it.
And then use params to key the encryptor and decryptor:
CFB_Mode< AES >::Encryption enc;
enc.SetKey( key, key.size(), params );
// CFB mode must not use padding. Specifying
// a scheme will result in an exception
StringSource ss1( plain, true,
new StreamTransformationFilter( enc,
new StringSink( cipher )
) // StreamTransformationFilter
); // StringSource
I believe your calls would look something like this (if I am parsing the OpenSSL correctly):
const byte* ptr = reinterpret_cast<const byte*>(sharedSecret.c_str());
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(ptr, 16));
CFB_Mode< AES >::Encryption enc;
enc.SetKey( ptr, 16, params );
In your production code you should use unique key and iv. So do something like this using HKDF:
std::string seed(AES_BLOCK_SIZE, '0');
std::generate(seed, seed + AES_BLOCK_SIZE,
std::bind(&RandomGenerator::GetInt, &m_RNG, 0, 255));
SecByteBlock sharedSecret(32);
const byte usage[] = "Key and IV v1";
HKDF<SHA256> hkdf;
hkdf.DeriveKey(sharedSecret, 32, &seed[0], 16, usage, COUNTOF(usage), nullptr, 0);
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(sharedSecret+16, 16));
CFB_Mode< AES >::Encryption enc;
enc.SetKey(sharedSecret+0, 0, params);
In the code above, sharedSecret is twice as large as it needs to be. You derive the key and iv from the seed using HDKF. sharedSecret+0 is the 16-byte key, and sharedSecret+16 is the 16-byte iv.
I've been trying to encrypt and decrypt a file using AES in GCM mode using Crypto++. What this code is supposed to do is, given a password, hash it using PBKDF2< HMAC< SHA256>>, then encrypt and decrypt a file using the password hash as the key. After searching all over on Stack Overflow I've gotten this far:
using namespace Cryptopp;
const std::string password = "password";
const int iterations = 1000000;
const std::string fileName = "test.txt";
SecByteBlock derived(32);
SecByteBlock salt(16);
const int TAG_SIZE = AES::BLOCKSIZE;
AutoSeededRandomPool rng;
rng.GenerateBlock(salt, 16);
// KDF function
PKCS5_PBKDF2_HMAC<SHA256> kdf;
kdf.DeriveKey(
derived.data(),
derived.size(),
0,
(byte*)password.data(),
password.size(),
salt.data(),
salt.size(),
iterations);
// Key the cipher
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(derived.data(), 16, derived.data() + 16, 16);
FileSource(fileName.c_str(), true,
new AuthenticatedEncryptionFilter(encryptor,
new FileSink(fileName.c_str()), false, TAG_SIZE));
// Key the cipher
GCM<AES>::Decryption decryptor;
decryptor.SetKeyWithIV(derived.data(), 16, derived.data() + 16, 16);
AuthenticatedDecryptionFilter decryptionFilter(decryptor,
new FileSink(fileName.c_str()), 16, TAG_SIZE);
FileSource(fileName.c_str(), true, new Redirector(decryptionFilter));
If the use of half of the derived hash from PBKDF2 as the key and half as the IV seems weird, this code is largely copied from How to use a custom key in Crypto++. Is this good practice in cryptography? Or should I generate a separate IV every time I encrypt?
Crypto++ throws a HashVerificationFailed exception, meaning the data was changed since encryption. So obviously I'm doing something wrong. What's wrong with my code?
I am not getting same session key after encoding and decoding it using below functions which uses crypto++ library:
CryptoPP::RSA::PrivateKey RSA_master_privKey;
CryptoPP::RSA::PublicKey RSA_master_pubKey;
std::string generate_Master_Keys()
{
std::string rsaParams;
try {
CryptoPP::InvertibleRSAFunction parameters;
RSA_master_privKey = CryptoPP::RSA::PrivateKey(parameters);
RSA_master_pubKey = CryptoPP::RSA::PublicKey(parameters);
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return rsaParams;
}
PAES_KEY_WITH_IV create_session_key(void)
{
CryptoPP::AutoSeededX917RNG<CryptoPP::AES> rng;
PAES_KEY_WITH_IV aes_info = new AES_KEY_WITH_IV;
try {
aes_info->key.resize(CryptoPP::AES::DEFAULT_KEYLENGTH);
rng.GenerateBlock(aes_info->key, aes_info->key.size());
aes_info->iv.resize(CryptoPP::AES::BLOCKSIZE);
rng.GenerateBlock(&aes_info->iv[0], aes_info->iv.size());
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return (aes_info);
}
std::string encrypt_session_key(PAES_KEY_WITH_IV pKey)
{
std::string ciphered;
CryptoPP::SecByteBlock block(pKey->key.size());
try {
CryptoPP::RSAES< CryptoPP::OAEP<CryptoPP::SHA> >::Encryptor enc(RSA_master_pubKey);
enc.Encrypt(rng, pKey->key, pKey->key.size(), block);
ciphered.assign((char *)block.BytePtr(), 192);
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return ciphered;
}
PAES_KEY_WITH_IV decrypt_session_key(std::string & ciphered)
{
CryptoPP::SecByteBlock rec(ciphered.size());
CryptoPP::SecByteBlock block((const byte *)ciphered.data(), ciphered.size());
PAES_KEY_WITH_IV pKey = new AES_KEY_WITH_IV;
try {
CryptoPP::RSAES< CryptoPP::OAEP<CryptoPP::SHA> >::Decryptor dec(RSA_master_privKey);
dec.Decrypt(rng, block, block.size(), rec);
pKey->key = rec;
}
catch (const CryptoPP::Exception& e)
{
std::cerr << e.what() << std::endl;
b_success = false;
}
return pKey;
}
Tailing of 192 bytes are not getting matched with original session key's bytes.
Can some one help me on this ?
Thanks in advance.
I am not getting same session key after encoding and decoding it using below functions
I think you are close to what you need. There's also an opportunity for improvement in the way you are doing it. I'll show you the improved way, and you can apply it to the existing method as well.
The improved way simply uses FixedMaxPlaintextLength, CiphertextLength and some friends to determine sizes. It also uses a technique from Integrated Encryption Schemes (IES).
First, transport the raw seed bytes, and not the {key, iv} pair. Then, when you need the {key, iv} pair, you derive the bytes you need from the seed bytes. Your derivation should include a usage label and a version number.
Second, the open question: how many bytes do you transport as seed bytes. That answer is FixedMaxPlaintextLength() or MaxPreimage() (I don't recall which). That's the size of the plaintext that can be encrypted under the scheme, and it depends on things like the modulus size and the padding scheme.
A lot of the code below is discussed at RSA Encryption Schemes and other places on the Crypto++ wiki. But its not readily apparent you need to visit them because you are still learning some of the techniques.
The following generates a random seed and encrypts it under the public key.
RSA_master_pubKey = RSA::PublicKey(parameters);
RSAES< OAEP<SHA> >::Encryptor enc(RSA_master_pubKey);
SecByteBlock seed(enc.FixedMaxPlaintextLength());
AutoSeededX917RNG<AES> rng;
rng.GenerateBlock(seed, seed.size());
SecByteBlock block(enc.CiphertextLength(seed.size())));
size_t req = enc.Encrypt(rng, seed, seed.size(), block);
block.resize(req);
// Transport block to peer as session seed
When the peer receives the encrypted seed block, they must decrypt it. Here's how to do it.
// Received from peer
SecByteBlock block(...);
RSAES< OAEP<SHA> >::Decryptor dec(RSA_master_privKey);
size_t req = dec.MaxPlaintextLength(block.size());
SecByteBlock seed(req);
DecodingResult result = dec.Decrypt(rng, block, block.size(), seed);
seed.resize(result.isValidCoding ? result.messageLength : 0);
You could even thrown an exception if result.isValidCoding returns false:
DecodingResult result = dec.Decrypt(rng, block, block.size(), seed);
if (!result.isValidCoding)
throw Exception(OTHER_ERROR, "Failed to decrypt seed bytes");
seed.resize(result.messageLength);
When you want to encrypt or decrypt with AES, you need to derive a key, iv and possibly an hmac key (are you authenticating the data?).
// Random seed from above
SecByteBlock seed;
HKDF<SHA256> kdf;
SecByteBlock aesKey(AES::DEFAULT_KEYLENGTH);
SecByteBlock aesIV(AES::BLOCKSIZE);
const byte aesLabel[] = "AES encryption key, version 1";
kdf.Derive(aesKey, aesKey.size(), seed, seed.size(), NULL, 0, aesLabel, COUNTOF(aesLabel));
const byte ivLabel[] = "AES initialization vector, version 1";
kdf.Derive(aesIV, aesIV.size(), seed, seed.size(), NULL, 0, ivLabel, COUNTOF(ivLabel));
IF you authenticate your data, then you can derive an HMAC key with the following. But generally speaking, you should probably use an Authenticated Encryption mode of operation:
const byte hmacLabel[] = "HMAC authentication key, version 1";
kdf.Derive(hmacKey, hmacKey.size(), seed, seed.size(), NULL, 0, hmacLabel, COUNTOF(hmacLabel));
HKDF was added at 5.6.3 or 5.6.4. If you don't have it, then grab hkdf.h from Wei Dai's GitHub (its header-only). By deriving from a base seed with unique labels, you are using a technique called independent derivation.
You add the labels and the version information to avoid gaps like discussed in Attacking and Repairing the WinZip Encryption Scheme. Also, using the entire FixedMaxPlaintextLength side steps some cryptographic attacks related to message length.
You might also want to look at Integrated Encryption Schemes (IES). We basically lifted the Key Encapsulation Mechanism (KEM) from IES. There's a Data Encapsulation Mechanism (DEM) that could be lifted, too.
If you are going to borrow the KEM and the DEM, then you may as well use the scheme. For that, see the following on the Crypto++ wiki:
Elliptic Curve Integrated Encryption Scheme
Discrete Logarithm Integrated Encryption Scheme
If you use one of the Integrated Encryption Schemes, then you are changing the underlying mathematical problem. RSA is Integer Factorization (IF), while IES is Diffie-Hellman and Discrete Logs (FF).
Using an Integrated Encryption Scheme is a good choice. Its IND-CCA2, which is a very strong notion of security. I believe it has better security properties than your original scheme.
I have downloaded the CryptoPP library and I am able to run the sample codes and get results (for CCM and GCM modes).
The next step for me is to try out the test vectors for each of these modes. From my understanding im suppose to try out the different keys, IVs and plaintexts as specified in the test vectors. Then I have to verify that the expected results are also specified in each vector.
What I can seem to understand is how to input these keys and IVs for the vectors. From the code as shown below, it seems to be using a random key.
Preferably I would like to input the keys and IVs from command prompt and then run the test code. Just setting the vectors from the code in Visual Studio would do though.
Please find the sample code and one of the vectors below:
Sample Code:
AutoSeededRandomPool prng;
SecByteBlock key( AES::DEFAULT_KEYLENGTH );
prng.GenerateBlock( key, key.size() );
byte iv[ AES::BLOCKSIZE * 16 ];
prng.GenerateBlock( iv, sizeof(iv) );
const int TAG_SIZE = 12;
// Plain text
string pdata="Authenticated Encryption";
// Encrypted, with Tag
string cipher, encoded;
// Recovered plain text
string rpdata;
/*********************************\
\*********************************/
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );
StringSource( pdata, true,
new AuthenticatedEncryptionFilter( e,
new StringSink( cipher ), false, TAG_SIZE
) // AuthenticatedEncryptionFilter
); // StringSource
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
/*********************************\
\*********************************/
try
{
GCM< AES >::Decryption d;
d.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );
AuthenticatedDecryptionFilter df( d,
new StringSink( rpdata ),
DEFAULT_FLAGS, TAG_SIZE
); // AuthenticatedDecryptionFilter
// The StringSource dtor will be called immediately
// after construction below. This will cause the
// destruction of objects it owns. To stop the
// behavior so we can get the decoding result from
// the DecryptionFilter, we must use a redirector
// or manually Put(...) into the filter without
// using a StringSource.
StringSource( cipher, true,
new Redirector( df /*, PASS_EVERYTHING */ )
); // StringSource
// If the object does not throw, here's the only
// opportunity to check the data's integrity
if( true == df.GetLastResult() ) {
cout << "recovered text: " << rpdata << endl;
}
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
One of the vectors:
GCM Test Case #14 (AES-256)
Variable Value
-------------------------------------------------
K : 00000000000000000000000000000000
: 00000000000000000000000000000000
P : 00000000000000000000000000000000
IV : 000000000000000000000000
H : dc95c078a2408989ad48a21492842087
Y_0 : 00000000000000000000000000000001
E(K,Y_0) : 530f8afbc74536b9a963b4f1c4cb738b
Y_1 : 00000000000000000000000000000002
E(K,Y_1) : cea7403d4d606b6e074ec5d3baf39d18
X_1 : fd6ab7586e556dba06d69cfe6223b262
len(A)||len(C) : 00000000000000000000000000000080
GHASH(H,A,C) : 83de425c5edc5d498f382c441041ca92
C : cea7403d4d606b6e074ec5d3baf39d18
T : d0d1c8a799996bf0265b98b5d48ab919
I have downloaded the CryptoPP library and I am able to run the sample codes and get results (for CCM and GCM modes).
Crypto++ does not use the examples from its wiki when running its self tests. The self test code is much more hairier.
What I can seem to understand is how to input these keys and IVs for the vectors. From the code as shown below, it seems to be using a random key.
The Crypto++ test vectors are located in <crypto++ dir>/TestVectors. I don't believe the vector you show in your question is from Crypto++. For example, here's from <crypto++ dir>/TestVectors/gcm.txt:
AlgorithmType: AuthenticatedSymmetricCipher
Name: AES/GCM
Source: aes-modes-src-07-10-08/Testvals/gcm.1, Basic Tests for GCM (compiled by B. R. Gladman)
Key: 00000000000000000000000000000000
IV: 000000000000000000000000
MAC: 00000000000000000000000000000000
Test: NotVerify
Key: 00000000000000000000000000000000
IV: 000000000000000000000000
MAC: 58e2fccefa7e3061367f1d57a4e7455a
Test: Encrypt
Key: 00000000000000000000000000000000
IV: 000000000000000000000000
Plaintext: 00000000000000000000000000000000
Ciphertext: 0388dace60b6a392f328c2b971b2fe78
MAC: ab6e47d42cec13bdf53a67b21257bddf
Test: Encrypt
...
You can see how the Crypto++ test suite consumes it when you use the cryptest.exe v command. The source files that execute the self tests are validat1.cpp, validat2.cpp and validat3.cpp. The GCM testing starts in validat1.cpp on line 95:
pass=ValidateGCM() && pass;
Here's ValidateGCM around line 1395:
bool ValidateGCM()
{
cout << "\nAES/GCM validation suite running...\n";
cout << "\n2K tables:";
bool pass = RunTestDataFile("TestVectors/gcm.txt", MakeParameters(Name::TableSize(), (int)2048));
cout << "\n64K tables:";
return RunTestDataFile("TestVectors/gcm.txt", MakeParameters(Name::TableSize(), (int)64*1024)) && pass;
}
Its a real pain to untangle RunTestDataFile, TestDataFile and TestAuthenticatedSymmetricCipher (and friends). They are implemented in datatest.cpp. The pain point is TestAuthenticatedSymmetricCipher around line 450 of datatest.cpp.
I usually go to the applicable standard, pull the test vectors, and then write my own self tests. In the case of deterministic encryption like AES/GCM, you can write a Known Answer Test (KAT). For non-deterministic tests, you will need to write a Pairwise Consistency Test (PCT). Essentially, you verify you can round trip data from a public/private key pair operation, like a DH or RSA key.
i used the example from the openssl demos for rsa encryption and decription , it work but the data after encryption was not the same as the text before encryption ., i need it to be the same .. so where can i modify the code so it can be return the same data size without any corruption of the encryption proccesss ., ?? Thanks in Advance ..
below is the code :
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include "loadkeys.h"
#define PUBFILE "cert.pem"
#define PRIVFILE "privkey.pem"
#define STDIN 0
#define STDOUT 1
int main()
{
char *ct = "This the clear text";
char *buf;
char *buf2;
EVP_PKEY *pubKey;
EVP_PKEY *privKey;
int len;
ERR_load_crypto_strings();
privKey = ReadPrivateKey(PRIVFILE);
if (!privKey)
{
ERR_print_errors_fp (stderr);
exit (1);
}
pubKey = ReadPublicKey(PUBFILE);
if(!pubKey)
{
EVP_PKEY_free(privKey);
fprintf(stderr,"Error: can't load public key");
exit(1);
}
/* No error checking */
buf = malloc(EVP_PKEY_size(pubKey));
buf2 = malloc(EVP_PKEY_size(pubKey));
len = RSA_public_encrypt(strlen(ct)+1, ct, buf, pubKey->pkey.rsa,RSA_PKCS1_PADDING);
if (len != EVP_PKEY_size(pubKey))
{
fprintf(stderr,"Error: ciphertext should match length of key\n");
exit(1);
}
printf("%d\n", strlen(buf));
printf("%d\n", strlen(ct));
RSA_private_decrypt(len, buf, buf2, privKey->pkey.rsa,RSA_PKCS1_PADDING);
printf("%s\n", buf2);
EVP_PKEY_free(privKey);
EVP_PKEY_free(pubKey);
free(buf);
free(buf2);
return 0;
}
I'm not sure you understand how RSA encryption works.
An RSA encryption produces a block of cipher text that is as wide as the modulus of the RSA key pair. This is not a negotiable attribute of the RSA encryption/decryption process. If you're using a 1024-bit RSA key, you're going to get a 128-byte cipher text for each "block" of cipher text input, and each block can range from 1 byte up to he size of the modulus (a little less, actually, read more about PKCS#1 standards here). Likewise, a 2048-bit key will generate a 256 byte cipher text .
RSA is expensive; indeed most-all asymmetric algorithms are. For this reason is is much more common to use RSA to encrypt a symmetric algorithm key (like a AES128 key), also called a "session" key, after using said key to encrypt your actual data, then sending both the encrypted session key and the encrypted data to the recipient. If the recipient has the proper private RSA key, they can decrypt the session key, then use that to symmetrically decrypt the actual data.
If you want small blocks of encrypted data, use symmetric encryption. You have much more flexibility in choices.
For more information on RSA Encryption, see here. For information on AES encryption, a standard for symmetric encryption, see here.