I have a question referring to the encryption code in this question:
Crypto++ encrypt and decrypt in two different c++ programs
If I want to use a custom key/iv, how can I do this?
If I want to use a custom key/iv, how can I do this?
Just plug it into a cipher with a mode. There are plenty of modes to choose from, but you should use an authenticated encryption mode like EAX, CCM or GCM. See Category:Mode for discussion of the modes in Crypto++.
The code below takes a password or secret, keys a cipher, and then encrypts and encodes a message. Next, it decodes the encrypted message. Finally it prints some of the parameters.
try {
// KDF parameters
string password = "Super secret password";
unsigned int iterations = 15000;
char purpose = 0; // unused by Crypto++
// 32 bytes of derived material. Used to key the cipher.
// 16 bytes are for the key, and 16 bytes are for the iv.
SecByteBlock derived(32);
// KDF function
PKCS5_PBKDF2_HMAC<SHA256> kdf;
kdf.DeriveKey(derived.data(), derived.size(), purpose, (byte*)password.data(), password.size(), NULL, 0, iterations);
// Encrypt a secret message
string plaintext = "Attack at dawn", ciphertext, recovered;
// Key the cipher
EAX<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(derived.data(), 16, derived.data() + 16, 16);
AuthenticatedEncryptionFilter ef(encryptor, new StringSink(ciphertext));
ef.Put((byte*)plaintext.data(), plaintext.size());
ef.MessageEnd();
// Key the cipher
EAX<AES>::Decryption decryptor;
decryptor.SetKeyWithIV(derived.data(), 16, derived.data() + 16, 16);
AuthenticatedDecryptionFilter df(decryptor, new StringSink(recovered));
df.Put((byte*)ciphertext.data(), ciphertext.size());
df.MessageEnd();
// Done with encryption and decryption
// Encode various parameters
HexEncoder encoder;
string key, iv, cipher;
encoder.Detach(new StringSink(key));
encoder.Put(derived.data(), 16);
encoder.MessageEnd();
encoder.Detach(new StringSink(iv));
encoder.Put(derived.data() + 16, 16);
encoder.MessageEnd();
encoder.Detach(new StringSink(cipher));
encoder.Put((byte*)ciphertext.data(), ciphertext.size());
encoder.MessageEnd();
// Print stuff
cout << "plaintext: " << plaintext << endl;
cout << "key: " << key << endl;
cout << "iv: " << iv << endl;
cout << "ciphertext: " << cipher << endl;
cout << "recovered: " << recovered << endl;
}
catch(CryptoPP::Exception& ex)
{
cerr << ex.what() << endl;
}
A run of the program produces the following output.
$ ./cryptopp-test.exe
plaintext: Attack at dawn
key: 7A8C7732898FB687669CB7DBEFBDD789
iv: 0AA980BABE72797E415C9B8979BF30EF
ciphertext: 197D0BD1A12577393AD1B1696B75D0FC6B8A142CF15B5F887AA965CE75F0
recovered: Attack at dawn
Even better, use an Integrated Encryption Scheme. Crypto++ provides two of them. The first is Elliptic Curve Integrated Encryption Scheme which operates over fields of elliptic curse. The second is Discrete Logarithm Integrated Encryption Scheme, which operates over the field of integers.
There's a number of non-obvious reason why its "even better", but the big one is its IND-CCA2. Other, more practical ones include: you can't reuse a security context because correct use is built into the system; and padding has been removed which greatly simplifies proofs and avoids potential oracles. The system is also predicated on Discrete Logs, which makes it a Diffie-Hellman based problem and its believed to be hard everywhere.
Related
I generate key to a file using the following function
std::filesystem::path create_key(std::filesystem::path folder_path)
{
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
prng.GenerateBlock(key, sizeof(key));
std::cout << "Size of key : " << sizeof(key) << std::endl;
std::cout << "Key before hex encode : " << (CryptoPP::byte*)&key << std::endl;
std::filesystem::path key_path = folder_path.concat("\key.aes");
// Key hex encoded to file
CryptoPP::StringSource key_s((CryptoPP::byte*)&key, sizeof(key), true,
new CryptoPP::HexEncoder(
new CryptoPP::FileSink(key_path.c_str())));
std::cout << key_path << "\n" << std::endl;
return key_path;
}
I use same function with few modifications for IV
I get the following results:
Size of key : 16
Key before hex encode : %
"C:\\Users\\User\\Desktop\\New folder\\key.aes"
Size of IV : 16
IV before hex encode : á▀┼┘ÅP⌐ûG→.JW╓‼Ñg79▓─G
"C:\\Users\\User\\Desktop\\New folder\\iv.aes"
Obviously after encoding the results look more readable ,
Does the key should use this characters ?
How can I read the the key and iv using crypto++
I have tried reversing the function :
CryptoPP::FileSource key(key_path.c_str(), sizeof(key), true,
new CryptoPP::HexDecoder(
new CryptoPP::StringSink((CryptoPP::byte*)&key),key_path.c_str()));
And use it by loading keys and IV but it didn't worked
I checked the key.aes file and found out it's created in ANSI encoding which make difference in few characters is there a way to make it utf-8
Console prints : ╢╘r#|ÿ┴♀[ÉB!±L↨SXêu:▄
Key.aes file : ¶Ôr#|˜Á[B!ñLS
converting key.aes file to utf-8 results : ¶Ôr#|˜Á[B!ñLS
This function is valid and works ,
No changes need to be added in my scenario.
What if I want to encrypt data, using the Crypto++ library and having a user-defined password that is shorter than 32 Byte?
Right now I have the following code:
byte passwordBytes[AES::MAX_KEYLENGTH];
byte ivBytes[AES::BLOCKSIZE];
std::string textToEncrypt("encryptMe");
std::string aesKey("passwordFromUser");
std::string ivText("Iv16BytesOfText...");
memset(passwordBytes, 0, sizeof(passwordBytes)); //fill with zeroes first
memcpy(passwordBytes, aesKey.data(), aesKey.size()); //fill with key data
memcpy(ivBytes, ivText.data(), CryptoPP::AES::BLOCKSIZE); //fill iv bytes
CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(passwordBytes, sizeof(passwordBytes), ivBytes);
StringSource encryptor(textToEncrypt, true,
new StreamTransformationFilter(encryption,
new StringSink(verschluesselterText)
,StreamTransformationFilter::NO_PADDING
)
);
As you can see, aesKey is shorter than 32 Bytes.
To apply the full 32 Bytes to the encrypting function, I just fill out the unused space with zeroes, but that doesn't seem to be the best solution to me.
Am I missing something regarding creating an AES Key? With a user-defined password?
My second question, what if the user chooses a password that is longer then 32 Byte? In My case, the password would be truncated, which doesn't sound right to me.
Thanks for your help!
What if i want to encrypt data, using the Crypto++ library and having a user defined password that is shorter then 32 Byte?
Use a key derivation function (KDF) to digest the password. The modern one is Krawczyk and Eronen's HKDF using the Extract-then-Expand model. The paper is located at Cryptographic Extraction and Key Derivation: The HKDF Scheme.
You should consider using it for the IV, too. Rather than deriving 32 bytes (AES::MAX_KEYLENGTH), derive 48 bytes (AES::MAX_KEYLENGTH+AES::BLOCKSIZE) instead. The IV in your design can then be used for the salt parameter to the KDF.
Maybe something like:
#include <iostream>
#include <string>
using namespace std;
#include "cryptlib.h"
#include "aes.h"
#include "sha.h"
#include "hkdf.h"
#include "modes.h"
#include "filters.h"
using namespace CryptoPP;
int main(int argc, char* argv[])
{
SecByteBlock key(AES::MAX_KEYLENGTH+AES::BLOCKSIZE);
string password("passwordFromUser"), iv("<random value>"), message("encryptMe");
string encrypted, recovered;
try
{
HKDF<SHA256> hkdf;
hkdf.DeriveKey(key, key.size(), (const byte*)password.data(), password.size(), (const byte*)iv.data(), iv.size(), NULL, 0);
///////////////////////////////////////////////////////////////////////
CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);
StringSource encryptor(message, true,
new StreamTransformationFilter(encryption,
new StringSink(encrypted))
);
///////////////////////////////////////////////////////////////////////
CTR_Mode<AES>::Decryption decryption;
decryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);
StringSource decryptor(encrypted, true,
new StreamTransformationFilter(decryption,
new StringSink(recovered))
);
cout << "Message: " << message << endl;
cout << "Recovered: " << recovered << endl;
}
catch(const Exception& ex)
{
cerr << ex.what() << endl;
return 1;
}
return 0;
}
When using the encryption method above, you have to track the {iv,message} pair. The IV is needed to ensure uniqueness per-message since the password effectively fixes the AES key.
What if the user chooses a password that is longer then 32 Byte? In My case, the password would by truncated, which doesn't sound right to me.
A KDF handles it for you. It extracts the entropy regardless of how little or how much.
StringSource encryptor(textToEncrypt, true,
new StreamTransformationFilter(encryption,
new StringSink(verschluesselterText),
StreamTransformationFilter::NO_PADDING
)
There's no need to specify the padding mode. Also see the documentation for BlockPaddingScheme.
You should be very careful with modes like CTR. CTR mode xor's the keystream with the plain text. If someone reuses their password on different messages, then its possible to recover the keystream which leads to plaintext recovery.
If ivText is unique for each message, then you should add it to your KDF to ensure a unique keystream for each message. Add the IV as the salt parameter for HKDF. Here, "unique" means if I have a message "Hello World", then the IV is different each time I encrypt the message.
If the IV is truly just "Iv16BytesOfText..." (i.e., its fixed), then there's nothing unique about it. Just derive an additional 16 bytes from the user's password. Then, to avoid the keystream xor attack, switch to a mode like CBC.
Finally, you should probably use CCM, EAX or GCM mode. Right now, you only have confidentiality. Usually you want authenticity, too. To gain authenticity, you often select an Authenticated Encryption mode of operation.
I am using Crypto++ library for cryptography related works. And sub-part of task is to encrypt and decrypt a text. The message can be up to 256 character long containing alphanumeric number spaces dot and special characters.
This piece of code is working for text length is less than or equal to 8. But after that it fails to decrypt the encrypted text.
// g++ -std=c++1y crypto.cpp -I /home/shravan40/cryptopp/build -lcryptopp
#include <iostream>
#include <cryptopp/rsa.h>
#include <cryptopp/integer.h>
#include <cryptopp/osrng.h>
int main(){
// Keys
CryptoPP::Integer n("0xbeaadb3d839f3b5f"), e("0x11"), d("0x21a5ae37b9959db9");
CryptoPP::RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);
CryptoPP::RSA::PublicKey pubKey;
pubKey.Initialize(n, e);
// Encryption
std::string message = "Shravan Kumar";
CryptoPP::Integer m((const byte *)message.data(), message.size());
std::cout << "m: " << m << std::endl;
// m: 126879297332596.
CryptoPP::Integer c = pubKey.ApplyFunction(m);
std::cout << "c: " << std::hex << c << std::endl;
// c: 3f47c32e8e17e291h
// Decryption
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::Integer r = privKey.CalculateInverse(prng, c);
std::cout << "r: " << std::hex << r << std::endl;
// r: 736563726574h
std::string recovered;
recovered.resize(r.MinEncodedSize());
r.Encode((byte *)recovered.data(), recovered.size());
std::cout << "recovered: " << recovered << std::endl;
// recovered: Expected : (Shravan Kumar), Received -> y5��dqm0
return 0;
}
Richard Critten is correct in his comment that usually hybrid encryption is used (an asymmetric cipher such as RSA with a symmetric cipher such as AES).
For these kind of insecure examples though you are usually simply required to split up the plaintext into parts the same size as the modulus n. So in your case just put every 8 bytes / characters together and use it for a (big endian) number. As the input seems to be ASCII the highest bit of those 8 bytes will always be set to zero, so you should not have any problems encrypting/decrypting. The input for RSA must of course always be less than n.
You may of course have to think up a smart way of handling the last part of the string.
Notes:
In case it hasn't been told (yet): raw RSA without padding is not secure either. So it is not just the key size that would be a problem if this example would be implemented in the field.
I haven't got a clue what you're doing with regard to decryption. You should have a look at your textbook again I suppose.
Integer m((const byte *)message.data(), message.size());
If you use message.size()+1, then the message will include the trailing NULL. You can use it during decryption to determine where the recovered string ends. Otherwise, you are going to need to track length of the message.
You might also be interested in Raw RSA from the Crypto++ wiki. But as Maarten cautined, its tricky to get right and build into a scheme.
You might consider something using RSA encryption with OAEP or PKCS v1.5 padding. Also see RSA Encryption Schemes on the Crypto++ wiki.
I believe this is undefined behavior:
std::string recovered;
recovered.resize(r.MinEncodedSize());
r.Encode((byte *)recovered.data(), recovered.size());
I think you need to use &recovered[0] to get the non-const pointer. It may be causing your problem.
I was using the Crypto++ library to do my arc4 encryption. Reference from here but not fully explained: http://www.cryptopp.com/wiki/Stream_Cipher.
The following is my code:
string key = "key";
string msg = "hello";
ARC4 arc4((byte*)key.c_str(), sizeof((byte*)key.c_str()));
arc4.ProcessData((byte*)msg.c_str(), (byte*)msg.c_str(), sizeof((byte*)msg.c_str()));
arc4.ProcessData((byte*)msg.c_str(), (byte*)msg.c_str(), sizeof((byte*)msg.c_str()));
cout << msg << endl;
My message after encrypt and decrypt which is totally garbage then i could not read.
not decrypted back to "hello" in short.
So how can I encrypt and decrypt message with key as above?
Two problems. First, you need to use the string's size(), and not sizeof(). Second, you need to reset the arc4 object when decrypting. Otherwise, the cipher's state is continued from the previous encryption.
string key = "key";
string msg = "hello";
ARC4 arc4((byte*)key.data(), key.size());
arc4.ProcessData((byte*)msg.data(), (byte*)msg.data(), msg.size());
// Reset state
arc4.SetKey((byte*)key.data(), key.size());
arc4.ProcessData((byte*)msg.data(), (byte*)msg.data(), msg.size());
cout << msg << endl;
I've implemented a C++ wrapper library for Crypto++ v5.6.2 and have a question about combinations of symmetric algorithms (e. g. Blowfish) and block modes (e. g. GCM).
I am able to encrypt and decrypt data via Blowfish/EAX, but I can't achieve the same by using Blowfish/GCM. AES/EAX and AES/GCM both work.
The following simple application demonstrates my problem:
#include <iostream>
#include <string>
#include "cryptopp/blowfish.h"
#include "cryptopp/filters.h"
#include "cryptopp/eax.h"
#include "cryptopp/gcm.h"
#include "cryptopp/osrng.h"
#include "cryptopp/hex.h"
std::string encrypt(
CryptoPP::AuthenticatedSymmetricCipher &encryption,
std::string const kPlainText,
CryptoPP::SecByteBlock const kKey,
unsigned const char * kIV) {
std::string cipher_text;
// TODO Is this the source of the problem?
// BlockSize always returns 0 which leads to an exception if GCM block mode is used!
std::cout << encryption.BlockSize() << " bytes" << std::endl;
encryption.SetKeyWithIV(
kKey,
kKey.size(),
kIV
);
CryptoPP::StringSink *string_sink = new CryptoPP::StringSink(cipher_text);
CryptoPP::BufferedTransformation *transformator = NULL;
// The AuthenticatedEncryptionFilter adds padding as required.
transformator = new CryptoPP::AuthenticatedEncryptionFilter(
encryption,
string_sink);
bool const kPumpAll = true;
CryptoPP::StringSource(
kPlainText,
kPumpAll,
transformator);
return cipher_text;
}
std::string decrypt(
CryptoPP::AuthenticatedSymmetricCipher &decryption,
std::string const kCipherText,
CryptoPP::SecByteBlock const kKey,
unsigned const char * kIV) {
std::string recovered_plain_text;
decryption.SetKeyWithIV(
kKey,
kKey.size(),
kIV);
CryptoPP::StringSink *string_sink = new CryptoPP::StringSink(
recovered_plain_text);
CryptoPP::BufferedTransformation *transformator = NULL;
CryptoPP::AuthenticatedDecryptionFilter *decryption_filter = NULL;
decryption_filter = new CryptoPP::AuthenticatedDecryptionFilter(
decryption,
string_sink);
transformator = new CryptoPP::Redirector(*decryption_filter);
bool const kPumpAll = true;
CryptoPP::StringSource(
kCipherText,
kPumpAll,
transformator);
return recovered_plain_text;
}
int main() {
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::SecByteBlock key(CryptoPP::Blowfish::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());
byte iv[CryptoPP::Blowfish::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));
// Creates templated mode objects of block ciphers.
// This works...
// CryptoPP::EAX<CryptoPP::Blowfish>::Encryption encryption;
// CryptoPP::EAX<CryptoPP::Blowfish>::Decryption decryption;
// This does NOT work...
CryptoPP::GCM<CryptoPP::Blowfish>::Encryption encryption;
CryptoPP::GCM<CryptoPP::Blowfish>::Decryption decryption;
std::string plain_text = "Block Mode Test";
std::string cipher_text = encrypt(encryption, plain_text, key, iv);
// terminate called after throwing an instance of 'CryptoPP::InvalidArgument'
// what(): Blowfish/GCM: block size of underlying block cipher is not 16
std::cout << "cipher text: " << std::hex << cipher_text << std::endl;
std::cout << "recovered plain text: " << decrypt(decryption, cipher_text, key, iv) << std::endl;
}
A CryptoPP::InvalidArgument exception is thrown if running the code above with the following text:
Blowfish/GCM: block size of underlying block cipher is not 16
But when running the code instead with the block mode EAX, no exception is thrown. So my questions are:
Does GCM only work with AES? Can GCM also be used with Blowfish or 3DES?
Is there a matrix available which lists all possible combinations of symmetric algorithms with block modes?
Or is this a bug in Crypto++? Because the method BlockSize() always returns 0 but the exception is only raised if using Blowfish (or 3DES) instead of AES. This seems to raise the exception mentioned.
GCM has been designed to work with 128-bit (=16 byte) block size only. You can find this in the original paper in Section 5.1.
Blowfish is a 64-bit block size algorithm, so the two are not compatible as an "out-of-the-box" authenticated encryption combination. The same is true for 3DES. The exception is not a bug in Crypto++.
GCM will work with other Crypto++ objects that have 128-bit block sizes. They include AES, Cast-256, Rijndael Cameilla, MARS, Serpent and Twofish. A table of the block sizes is available at Applied Crypto++: Block Ciphers.
GCM will not work with larger block sizes either. For example, Rijndael (the parent of AES) offers 192-bit and 256-bit block sizes (AES only specifies the 128-bit block size). GCM will not work with the larger block sizes. And the same is true for SHACAL-2, with a 256-bit block size.
Crypto++'s BlockSize() sometimes returns 0 (it has to do with the template parameters). Instead, use the compile time constants like AES::BLOCKSIZE, Camellia::BLOCKSIZE and Rijndael::BLOCKSIZE. This could be considered a bug.