Crypto++ :Encrypt in Python ,decipher in C++ - python

I am trying to do the following:In a python script I use pycrypto lib to encrypt some text.Then I save it to file.Then I load that file and decode the encrypted text using the same key I used in Python.It fails at stfDecryptor.MessageEnd(); with the error:
"CryptoCPP::InvalidCiphertext at memory location [some memory]
Here is my code:
Python:
from Crypto.Cipher import AES
BLOCK_SIZE = 16
PADDING = '{'
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: c.encrypt(pad(s))
secret = 'MyKey123456789ab'
# create a cipher object using the random secret
cipher = AES.new(secret)
# encode a string
encoded = EncodeAES(cipher, textIn)
#save to file
fileOut = open("enc_shader.vert","w")
fileOut.write(encoded)
fileOut.close()
CPP :
std::string key = "MyKey123456789ab";
std::string iv = "aaaaaaaaaaaaaaaa";
std::ifstream fileIn("enc_shader.vert");
std::stringstream buffer;
buffer << fileIn.rdbuf();
std::string ciphertext1 = buffer.str();
CryptoPP::AES::Decryption aesDecryption((byte*)key.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, (byte*)iv.c_str() );
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( ciphertext1.c_str() ), ciphertext1.size() );
stfDecryptor.MessageEnd();//fails here.
From what I read these to endpoints should work as pycrypto just a wrapper for the CryptoCPP lib.May be I miss the padding on CPP side?
UPDATE:
Ok,I found that changing the padding scheme:
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) ,BlockPaddingSchemeDef::NO_PADDING);
decodes the string on CPP side.But the decoded string contains the padding chars.
So if the original string was "aaaaaaaaaaaaaaaaa"
The decoded string looks like this:
"aaaaaaaaaaaaaaaaa{{{{{{{{{{{{{{{"
15 bytes were added to pad to 32 bytes.
Why Crypto++ doesn't remove those at decryption?

Your Python encryption code manually adds '{' characters to pad to the block size. This is not a defined padding mode, so the Crypto++ code will not be able to remove the padding using an integrated padding scheme. In other words, you should decrypt using NO_PADDING and then remove the padding yourself.
But it would be better to let the Python code use PKCS#7 padding, so you can use PKCS_PADDING as option within Crypto++.

Related

AES Python - Different output than expected

I try to make a AES ecryption script for a HEX file in python, which should then be decrypted on a microcontroller. At the moment I want to encrypt a test array (hex, 16-byte), which I already did successfully on the microcontroller, but phyton seems to do something different.
I expected the 'expected' output when encrypted, but it gives me a much larger output, but the AES block size is 16 byte, so it should work. When I have a look at the size of the iv or password after unhexlify, it states 49, that seems totally wrong. What am I doing wrong here?
from base64 import b64encode, b64decode
from binascii import unhexlify
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
iv = "000102030405060708090A0B0C0D0E0F"
password = "2b7e151628aed2a6abf7158809cf4f3c"
msg = "6bc1bee22e409f96e93d7e117393172a"
expected = "7649abac8119b246cee98e9b12e9197d"
print(f"IV: {iv}")
print(f"PWD: {password}")
print(f"MSG: {msg}")
# Convert Hex String to Binary
iv = unhexlify(iv)
password = unhexlify(password)
# Pad to AES Block Size
msg = pad(msg.encode(), AES.block_size)
print(f"IV SIZE: {iv.__sizeof__()}")
print(f"PSW SIZE: {password.__sizeof__()}")
print(f"MSG SIZE: {msg.__sizeof__()}")
# Encipher Text
cipher = AES.new(password, AES.MODE_CBC, iv)
cipher_text = cipher.encrypt(msg)
print(cipher_text)
# Encode Cipher_text as Base 64 and decode to String
out = b64encode(cipher_text).decode('utf-8')
print(f"OUT: {out}")
# Decipher cipher text
decipher = AES.new(password, AES.MODE_CBC, iv)
# UnPad Based on AES Block Size
plaintext = unpad(decipher.decrypt(b64decode(out)), AES.block_size).decode('utf-8')
print(f'PT: {plaintext}')
Edit: When I use len(IV) instead of size, it gives the correct length. The problem is still, that the message length is somehow 48-bytes, although the AES.block_size is 16 bytes
The expected value for the ciphertext is produced when 1st the plaintext is not padded (the padding is not necessary because the length of the plaintext satisfies the length criterion, according to which the length must be an integer multiple of the blocksize, 16 bytes for AES), 2nd the message is hex decoded and 3rd the ciphertext is hex encoded.
I.e. you have to replace
msg = pad(msg.encode(), AES.block_size)
with
msg = unhexlify(msg)
and hex encode the ciphertext for the output (to get the expected value):
print(hexlify(cipher_text).decode('utf-8'))
Similarly, no unpadding may be performed during decryption and the message must be hex encoded and not UTF-8 decoded.
I.e. you have to replace
plaintext = unpad(decipher.decrypt(b64decode(out)), AES.block_size).decode('utf-8')
with
plaintext = hexlify(decipher.decrypt(b64decode(out))).decode('utf-8')
Regarding the length, you have already recognized that __sizeof__() is the wrong function and that len() should be used.

How do you encrypt an image using CTR encryption?

Good afternoon, I am trying to write a program to read a .bmp file and encrypt it using the given initial value using 𝑐𝑡𝑟 and the one-time pad.
The first 36 bytes form the header of the image and are not encrypted, but just copied to the new file
The image data beginning at 0x36 to the end are grouped into four-byte words and each word is encrypted using 𝑐𝑡𝑟.
To avoid changing the size of the image, do not include 𝑐0=𝑐𝑡𝑟 in the encrypted image.
As of now, this is what I have:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Util import Counter
filename = "Image11.bmp"
filename_out = "Image11Encrypted.bmp"
key = 0xe0984dd3
bkey = key.to_bytes(32, 'big')
cipher = AES.new(bkey, AES.MODE_CTR, initial_value= 0xff128eff)
def encrypt(filename, filename_out, key):
with open(filename, "rb") as f:
clear = f.read()
clear_trimmed = clear[64:-2]
ciphertext = clear_trimmed
ciphertext = cipher.encrypt(pad(clear_trimmed, 16))
ciphertext = clear[0:64] + ciphertext + clear[-2:]
with open(filename_out, "wb") as f:
f.write(ciphertext)
encrypt(filename, filename_out, key)
print("Encrypted using AES in CTR mode and saved to \"" + filename_out + "\"")
However, I keep running into this Error:
.
Any help would be great, not sure where to go from here
The configuration of the CTR mode with PyCryptodome is described here. There are two ways to specify the counter block: By setting a nonce (parameter nonce) and a start value (parameter initial_value). If no nonce is specified, a random nonce of half the block size is generated implicitly.
The other way is to define a counter block object (parameter counter), which can be used to specify the components of the counter block in detail (prefix, counter, suffix).
If only the start value is to be specified, the parameter initial_value must be used instead of counter:
cipher = AES.new(bkey, AES.MODE_CTR, initial_value=0xff128eff)
As mentioned above, this implicitly creates a random nonce with half the block size, which can be determined with cipher.nonce.
Please note: The code lacks the determination of the 16 byte IV, which is needed for decryption. The IV consists of nonce and counter and is usually placed on byte level before the ciphertext.Furthermore, according to the question the first 36 bytes should not be encrypted, a little later it is stated that the data starts at 0x36 (=54) and in the code 64 is used as the beginning of the data. This seems to be inconsistent.

Coverting Simple3Des(VB.net) encryption-decryption code to python

I'm have a plain JSON text which is encrypted using Triple-DES in Visual Basic(code provided below), now I'm trying to decrypt it using python language. when I test it I'm getting the wrong output.
I think there is any padding issue because I didn't get this line:
ReDim Preserve hash(length - 1)
VB code:
Public NotInheritable Class Simple3Des
Private TripleDes As New TripleDESCryptoServiceProvider
Private Function TruncateHash(ByVal key As String, ByVal length As Integer) As Byte()
Dim sha1 As New SHA1CryptoServiceProvider
' Hash the key.
Dim keyBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(key)
Dim hash() As Byte = sha1.ComputeHash(keyBytes)
' Truncate or pad the hash.
ReDim Preserve hash(length - 1)
Return hash
End Function
Sub New(ByVal key As String)
' Initialize the crypto provider.
TripleDes.Key = TruncateHash(key, TripleDes.KeySize \ 8)
TripleDes.IV = TruncateHash("", TripleDes.BlockSize \ 8)
End Sub
Public Function EncryptData(ByVal plaintext As String) As String
' Convert the plaintext string to a byte array.
Dim plaintextBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(plaintext)
' Create the stream.
Dim ms As New System.IO.MemoryStream
' Create the encoder to write to the stream.
Dim encStream As New CryptoStream(ms,
TripleDes.CreateEncryptor(),
System.Security.Cryptography.CryptoStreamMode.Write)
' Use the crypto stream to write the byte array to the stream.
encStream.Write(plaintextBytes, 0, plaintextBytes.Length)
encStream.FlushFinalBlock()
' Convert the encrypted stream to a printable string.
Return Convert.ToBase64String(ms.ToArray)
End Function
Public Function DecryptData(ByVal encryptedtext As String) As String
' Convert the encrypted text string to a byte array.
Dim encryptedBytes() As Byte = Convert.FromBase64String(encryptedtext)
' Create the stream.
Dim ms As New System.IO.MemoryStream
' Create the decoder to write to the stream.
Dim decStream As New CryptoStream(ms,
TripleDes.CreateDecryptor(),
System.Security.Cryptography.CryptoStreamMode.Write)
' Use the crypto stream to write the byte array to the stream.
decStream.Write(encryptedBytes, 0, encryptedBytes.Length)
decStream.FlushFinalBlock()
' Convert the plaintext stream to a string.
Return System.Text.Encoding.Unicode.GetString(ms.ToArray)
End Function
End Class
Here is my approach with different paddings and combinations:
from pyDes import *
import hashlib
import base64
key = b'Hi4Q=rLJnyqPj$G_cTqDcwgWo'
sha1_hash = hashlib.sha1(key).digest()
key_ = sha1_hash[:len(key)//8]
# key_ = sha1_hash[:-4]
# key_ = sha1_hash
base64Encrypted = r'''...'''
base64Decrypted = base64.b64decode(base64Encrypted)
print ("output: ", k.decrypt(base64Decrypted))
Am I doing something wrong?

Decrypting (in python) the encrypted data using AES256 (CBC + PKCS7 padding) is resulting extra characters in the end

We are using below java code to decrypt the data which is encrypted using AES-256 in CBC mode and PKCS7 padding.
Java Code:
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
public class AES256 {
private static byte[] initVector = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public String decrypt (String encryptedDataBase64, String keyBase64)
{
try {
Security.setProperty("crypto.policy", "unlimited");
IvParameterSpec ivSpec = new IvParameterSpec(initVector); // Get the init vector
// Get the Base64-encoded key
byte[] key = Base64.decodeBase64(keyBase64.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); // AES / CBC / PKCS5 padding
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] encryptedData = Base64.decodeBase64(encryptedDataBase64.getBytes("UTF-8"));
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData);
}
catch (Exception e) {
logger.error("AES256 Decrypt: Decryption exception: "+ e.getMessage());
return null;
}
}
}
Now we need to convert this decryption logic to Python as our app is sending the encrypted data in the headers while requesting for index.html from server. I tried to decrypt using Crypto. It is giving the decrypted string but also some additional characters in the end.
import base64
from Crypto.Cipher import AES
key = base64.b64decode(myKeyBase64)
enc = base64.b64decode(encDataBase64)
ivBase = base64.b64decode('AAAAAAAAAAAAAAAAAAAAAA==');
cipher = AES.new(key, AES.MODE_CBC, ivBase)
cipher.decrypt(enc).decode('utf-8')
It is decrypting properly but in the end it is giving some extra characters which are not in the original string like 'myText\x06\x06\x06\x06\x06\x06'.
I tried this after reading some of stack over flow questions. Can any one please let me know if there is any error in the code.
How to decode data encrypted using AES-256 in CBC mode and PKCS7 padding?
Encrypt & Decrypt using PyCrypto AES 256
To encrypt a byte array with AES you do need exactly a 16 byte long array - then you could use a '...NoPadding'.
Having e.g. a 13 character long string and you transform the string to a byte array this array is 13 bytes long. Using PKCS5 the byte array is filled up with 3 bytes of value x03. If you need to fill up 7 characters all 7 bytes will have the value x07, missing 10 chars result in 10 bytes of x0a.
To strip of the padding just read the last byte (e.g. x0a = '10') and remove the last 10 bytes to get the original string.
On Java-side the naming is PKCS5Padding that is (mostly) identical to PKCS7Padding (this naming is used in other frameworks/languages).

TypeError while using pycrypto library in python 3.3.2

I have just started using PyCrypto package for python.
I am trying out the following code under python 3.3.2:
Code Reference : AES Encryption using python
#!/usr/bin/env python
from Crypto.Cipher import AES
import base64
import os
# the block size for the cipher object; must be 16, 24, or 32 for AES
BLOCK_SIZE = 32
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
PADDING = '{'
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
# generate a random secret key
secret = os.urandom(BLOCK_SIZE)
# create a cipher object using the random secret
cipher = AES.new(secret)
# encode a string
encoded = EncodeAES(cipher, 'password')
print ('Encrypted string:', encoded)
# decode the encoded string
decoded = DecodeAES(cipher, encoded)
print ('Decrypted string:', decoded)
The error that I run into is :
Traceback (most recent call last):
File "C:/Users/Hassan Javaid/Documents/Python files/crypto_example.py", line 34, in <module>
decoded = DecodeAES(cipher, encoded)
File "C:/Users/Hassan Javaid/Documents/Python files/crypto_example.py", line 21, in <lambda>
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
TypeError: Type str doesn't support the buffer API
Any pointers to why I am getting the same ?
This is because cipher.encrypt(plain_text) in python 3.x returns a byte string.
The example given in the page uses python 2.x in which case cipher.encrypt(plain_text) returned a regular string.
You can verify the same by using the type function:
In python 3.x:
>>> type(cipher.encrypt("ABCDEFGHIJKLMNOP"))
<class 'bytes'>
In python 2.x
>>> type(cipher.encrypt("ABCDEFGHIJKLMNOP"))
<class 'str'>
The error you are getting is because you are trying to use the rstrip method on a byte string.
Use:
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).decode("UTF-8").rstrip(PADDING)
This will decode the bytestring to regular string before using the rstrip method on it.
Another way to look at it is that the method rstrip accepts as argument a byte string if invoked on a byte string, or a regular string if invoked on a regular string.
Since decrypt of an AES object returns a byte string, DELIMITER should be defined as a byte string too:
PADDING = b'{'

Categories