I have a python endpoint that encrypts string using AES cbc mode and returns it to the client software written in c (in a hex space separated format ) The link for the c repo
std::vector<unsigned char> cipher_as_chars(std::string cipher)
{
std::istringstream strm{cipher};
strm >> std::hex;
std::vector<unsigned char> res;
res.reserve(cipher.size() / 3 1);
int h;
while (strm >> h) {
res.push_back(static_cast<unsigned char>(h));
}
return res;
}
namespace client{
std::string decrypt_cipher(std::string cipher, std::string usr_key)
{
std::string original_text = "";
const std::vector<unsigned char> key = key_from_string(usr_key); // 16-char = 128-bit
const unsigned char iv[16] = {
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x56, 0x34, 0x35, 0x36
};
std::vector<unsigned char> encrypted = cipher_as_chars(cipher);
unsigned long padded_size = 0;
std::vector<unsigned char> decrypted(encrypted.size());
plusaes::decrypt_cbc(&encrypted[0], encrypted.size(), &key[0], key.size(), &iv, &decrypted[0], decrypted.size(), &padded_size);
for (int i =0 ; i < decrypted.size(); i )
{
//cout << decrypted[i] << endl;
std::stringstream stream;
stream << decrypted[i];
original_text = original_text stream.str();
}
return original_text;
}
}
def encrypt_string(key,text):
result = ''
while len(text)% 16 != 0 :
text = text " "
string_as_bytes = text.encode('utf8')
obj = AES.new(key.encode("utf8"), AES.MODE_CBC, 'This is an IV456'.encode("utf8"))
cipher_text = obj.encrypt(string_as_bytes)
for item in bytearray(cipher_text):
result = f"{hex(item).replace('0x','')} "
return result
@api.route('/test')
def test_route():
return encrypt_string("Encryptionkey123", "Happy new year people")
The server has an encryption and decryption function same with the client software if I encrypt a string using the c code and decrypt it using decryption function written in c it works fine and I get the same string, but when the client reads the response of /test
and encrypts it and the c client tries to decrypt it outputs the string missing n letters in the end
The original text in python server Happy new year people
The output in the c client Happy new year p
CodePudding user response:
What's wrong is that the C library uses PKCS#7 padding and the python code uses spaces. The horrid C library doesn't check if the padding is within the block size nor the padding, so it directly unpads given the value of the final character found - for space this is 0x20
. So this will unpad 32 bytes instead of any normal padding size (I wonder what it does when the padding is larger than the remaining plaintext size - possibly a buffer underflow?).
You have to implement PKCS#7 padding in the python. This is adding 1 byte valued 0x01
to 16 bytes valued 0x10
. You can see an implementation here. Note that the unpadding shown there is as vulnerable as in the C library: instead you 1 check if the last padding byte is in the range 1..<blocksize=16> and then check if all the other padding bytes are correct as well, creating an exception or error code if not.
Also note CBC padding oracles, the use of a static IV etc. Use TLS for transport security.
CodePudding user response:
Look at this example pycrypto
does support pkcs#7 padding your take on padding is poor, just use the built-in padding function in that module
Example take from the link
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad
key=b'1234567890123456'
cipher=AES.new(key,AES.MODE_CBC)
text=b'secret text'
padtext=pad(text,16,style='pkcs7')
cipherText=cipher.encrypt(padtext)
print(padtext)
print(cipherText)
plaintext=cipher.decrypt(cipherText) #can't use same object to decrypt
print(plaintext)
you might want to add some code to turn the result into a hex separated string