I am writing an encryption class using Crypto++. The idea is that the client connects using RSA (encrypt, decrypt) with username, password, 32 byte key. The server will then return using the stream cipher (simEncrypt
, simDecrypt
) a session ID. Then any communication between the systems uses the stream cipher.
What would I liked reviewed:
- Security: Is this a secure way of communicating?
- General: Can the code be easily read and formatting?
#pragma once
#include <cstdlib>
#include <crypto++/rsa.h>
#include <crypto++/osrng.h>
#include <crypto++/base64.h>
#include <crypto++/files.h>
#include <crypto++/salsa.h>
using namespace CryptoPP;
class encryption {
std::string privFileKeyStr;
std::string publicFileKeyStr;
RSAES_OAEP_SHA_Decryptor privKey;
RSAES_OAEP_SHA_Encryptor pubKey;
AutoSeededRandomPool rng;
const byte msgGroupDecryptedLength = 40;
unsigned int msgGroupEncryptedLength;
std::string SimEncryptDo(std::string key, std::string strShortString, byte iv[8]) {
byte *plaintextBytes = (byte *)strShortString.c_str();
byte *ciphertextBytes = new byte[strShortString.length()];
byte* keyBytes = (byte *)key.substr(0, 32).c_str();
Salsa20::Encryption salsa;
salsa.SetKeyWithIV(keyBytes, 32, iv);
salsa.ProcessData(ciphertextBytes, plaintextBytes, strShortString.length());
std::string returnStr((const char *)ciphertextBytes, strShortString.length());
salsa.SetKeyWithIV(keyBytes, 32, iv);
salsa.ProcessData(plaintextBytes, ciphertextBytes, strShortString.length());
delete ciphertextBytes;
return returnStr;
}
public:
encryption()
{
privFileKeyStr = "privkey.txt";
publicFileKeyStr = "pubkey.txt";
}
encryption(std::string privKeyStr, std::string pubKeyStr) {
privFileKeyStr = privFileKeyStr;
publicFileKeyStr = publicFileKeyStr;
}
void GenerateKey()
{
AutoSeededRandomPool rng;
InvertibleRSAFunction privkey;
privkey.Initialize(rng, 1024);
Base64Encoder privkeysink(new FileSink(privFileKeyStr.c_str()));
privkey.DEREncode(privkeysink);
privkeysink.MessageEnd();
RSAFunction pubkey(privkey);
Base64Encoder pubkeysink(new FileSink(publicFileKeyStr.c_str()));
pubkey.DEREncode(pubkeysink);
pubkeysink.MessageEnd();
}
void LoadKeys() {
RSAES_OAEP_SHA_Encryptor lPubKey(
FileSource(publicFileKeyStr.c_str(), true, new Base64Decoder)
);
pubKey = lPubKey;
msgGroupEncryptedLength = (unsigned int)pubKey.CiphertextLength(5);
RSAES_OAEP_SHA_Decryptor lPrivKey(
FileSource(privFileKeyStr.c_str(), true, new Base64Decoder)
);
privKey = lPrivKey;
}
std::string Encrypt(std::string strShortString) {
std::string Builder = "";
std::string stringPart = "";
for (unsigned int i = 0; i < (unsigned int)strShortString.size(); i = i + msgGroupDecryptedLength) {
stringPart = strShortString.substr(i, msgGroupDecryptedLength);
byte randLength = rng.GenerateByte() / 8;
byte msgLength = (byte)stringPart.size();
SecByteBlock randomPadLeft(randLength);
rng.GenerateBlock(randomPadLeft, randLength);
std::string randomPadLeftStr((const char*)randomPadLeft.begin(), randomPadLeft.size());
std::string toEncrypt(1, (unsigned char)randLength);
toEncrypt.append(std::string(1, (unsigned char)msgLength));
toEncrypt.append(randomPadLeftStr);
toEncrypt.append(stringPart);
size_t paddingRight = 80 - toEncrypt.size();
SecByteBlock randomPadRight(paddingRight);
rng.GenerateBlock(randomPadRight, paddingRight);
std::string randomPadRightStr((const char*)randomPadRight.begin(), randomPadRight.size());
toEncrypt.append(randomPadRightStr);
SecByteBlock sbbCipherText(msgGroupEncryptedLength);
pubKey.Encrypt(
rng,
(byte const*)toEncrypt.data(),
toEncrypt.size(),
sbbCipherText.begin());
Builder.append(std::string((const char*)sbbCipherText.begin(), sbbCipherText.size()));
}
return Builder;
}
std::string Decrypt(std::string strShortString) {
std::string decryptedBuilder;
for (unsigned int i = 0; i < (unsigned int)strShortString.size(); i = i + msgGroupEncryptedLength) {
std::string msgGroup = strShortString.substr(i, msgGroupEncryptedLength);
SecByteBlock sbbCipherText(msgGroupEncryptedLength);
privKey.Decrypt(
rng,
(byte const*)msgGroup.data(),
msgGroup.size(),
sbbCipherText.begin());
std::string decrypted((const char*)sbbCipherText.begin(), sbbCipherText.size());
byte ind1 = decrypted.at(0);
byte ind2 = decrypted.at(1);
decryptedBuilder.append(decrypted.substr(ind1 + 2, ind2));
}
return decryptedBuilder;
}
std::string simDecrypt(std::string key, std::string strShortString) {
std::string keyStr = strShortString.substr(0, 8);
std::string encryptStr = strShortString.substr(8, strShortString.size() - 8);
byte* iv = (byte*)keyStr.c_str();
return SimEncryptDo(key, encryptStr, iv);
}
std::string simEncrypt(std::string key, std::string strShortString) {
byte iv[8];
rng.GenerateBlock(iv, 8);
std::string encStr = SimEncryptDo(key, strShortString, iv);
std::string returnStr(std::string((char *)iv, 8));
returnStr.append(encStr);
return returnStr;
}
};
A brief explanation of AES and Salsa:
AES uses two keys. A message encrypted by one is decrypted by the other but can only encrypt a short amount of characters (which is why the loop is in the two functions).
Salsa takes a key and IV and will encrypt the message. If you put the encrypted message in, it will return the message.
delete ciphertextBytes;
should bedelete [] ciphertextBytes;
\$\endgroup\$