How to encrypt with AES in Python with no salt? - python

I want to encrypt some data using AES in Python such that two encryptions of the same data give the same output (using the same key in both encryptions). I tried to use a static IV but I still get two different outputs given one input.
Here is what I did:
from Crypto.Cipher import AES
iv = 16 * '\x00'
cipher = AES.new(key, AES.MODE_CBC, iv)
I would like to obtain the same cipher if I decide to encrypt the same data multiple times.
Do you know how to prevent the use of a salt?
Thank you!

The cipher object here is the state and is initialized using the provided key data and initialization vector. Once you start to use this initialized keystate it is modified for each block you encrypt when you use CBC mode. That is the cipher block chaining in action. To have two separate encryptions you need to use two separate keystates. Or, as mentioned, you can use a mode (ECB) that does not affect the keystate when encrypting a block.
A demo (python3):
from Crypto.Cipher import AES
key = 16 * b'\0'
iv = 16 * b'\0'
plaintext = 16 * b'\x41'
a = AES.new(key, AES.MODE_CBC, iv)
b = AES.new(key, AES.MODE_CBC, iv)
ar = a.encrypt(plaintext)
br = b.encrypt(plaintext)
>>> ar
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
>>> br
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
The disadvantage of ECB mode is everytime we encrypt this plaintext with this key and iv we will get this result. With CBC mode, each time we encrypt another block the keystate is changed and we get a different output which is dependent upon what has been encrypted before.
ECB Mode
>>> ecb = AES.new(16 * b'\0', AES.MODE_ECB)
>>> ecb.encrypt(16 * b'\x41')
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
>>> ecb.encrypt(16 * b'\x41')
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
Each encryption produces the same result. No iv required.
CBC Mode
>>> cbc = AES.new(16 * b'\0', AES.MODE_CBC, 16 * b'\0')
>>> cbc.encrypt(16 * b'\x41')
b'\xb4\x9c\xbf\x19\xd3W\xe6\xe1\xf6\x84\\0\xfd[c\xe3'
>>> cbc.encrypt(16 * b'\x41')
b'o\x9fv\x9a\x9c\xaa\x03\x13\xba\x17\x8c\x1c.\x0f`v'
>>> cbc.encrypt(16 * b'\x41')
b'\n1\x165\xb5?\x04h\xa8\r>k\xdbK\xbc4'
The first block encrypts the same as ECB mode but subsequent blocks are different.
Futher discussion about modes of operation for block ciphers can be found online.

Related

How to write One-Time Pad cipher in Python

Based on Wikipedia, One-Time Pad is an encryption technique that cannot be cracked. How to implement One-Time Pad encryption in Python?
Here is an example implementation
def encrypt(pad, secret):
return ''.join([chr(ord(c) ^ ord(secret[i])) for i, c in enumerate(pad[:len(secret)])])
Now you can use it to encrypt your messages. Remember the pad size should be at least the size of the secret:
secret = 'i am a secret'
pad = '33116f14-9b6f-42c6-9636-3dbd31c0548d'
enc = encrypt(pad, secret)
And you can decipher it with the same function:
assert secret == encrypt(pad, enc)

Perfroming AES CBC 128 Cannot get the same result same the ONline tools

I using getting the encrypted message from a third party sever.
I am using the tools in enter link description here
to test and its correct
The Encrypted Text =
"WiI9g5qo+ztSlqHMbpiezHZ2dBkQ2gprGJZyWtwcMTWPoxzLsMmujE9xDeFK4XYMfBdZGh2naMwP3LfbPy/06mazrSs66WRM1oxhz56L2UzTKyCWCl+ld7RlN7aPwfEw2j9VN50YCkMLfQRfIAXTspKQb6o5QQw8ey0cINdtWSHClz/uXiCFqiYJfItxY1rAZkE1Qj0b0izGQFJ9/44Zfw0dJtzCXYgXTPZftPeGTdoX/HnZJpUvfqmLIAdgAyoXi5BxL5bgSs30yaB4bRxJJj7DKpVbAgZmx0ecjmiGDh7t78A16pZ2kz+OIUkuc/hxvUaVehsH1pVdqycpUJfbgy+to0AY/+BBd38GGvv8YdTCa99bSHRGaZuUglLKN/2J0pZmfrIARIdgrV2yDK+IN4hTVKf1jprtfvhvkG+eRyDfoLL9rg8+ZEtdYUdZgDdF3ftmHKTzgxI6leMWX7WFRTHjxVYFVk0yWA9xXk6s/WcG6IFeGYPVF94IcLeC2eAjaMasusF+C6qyFWi6nuyFK2Gr1utvG6kg84Hu0KKYg42MHXIR1AtQW3MWaqosb54y0GutQtnD47l84/PdJvUhuE/a7uyfCjjtyh2sRRLX3WDosyRZsqjLea9EIX6oNmQMZd1WRxM86Ggt6bVOc9KY5Z7HLpLyb0lLF4sdyzfBNJB7u7vqkBzsEss1Yq+sXD0N"
Key = "1234567812345678"
And the result
After AES Decrypted Output (Base64):
And after Decode Plain Text
THe result is :
{"upPacketSN":-1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619672797621,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQyNTksMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
One of the thing is that IV must be NULL to get the correct answer.
And so if i implement in Python AES CBC using pycryptodome library in the following like:
class AES_CBC:
def add_to_16(self, value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 返回bytes
#解密方法
def decrypt_oralce(self, key, text):
# 初始化加密器
# 偏移量 16个0
iv = "0000000000000000"
aes = AES.new(self.add_to_16(key), AES.MODE_CBC, self.add_to_16(iv))
#优先逆向解密base64成bytes
base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
#
decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8') # 执行解密密并转码返回str
unpad = lambda s : s[0:-ord(s[-1])]
#PADDING = '\0'
#print decrypted_text.rstrip(PADDING) #zeropadding只见诶去掉结尾\0
# print(unpad(decrypted_text))
return unpad(decrypted_text)
if __name__ == '__main__':
aes = AES_CBC()
#加密
key = "1234567812345678"
enc_msg = "WiI9g5qo+ztSlqHMbpiezHZ2dBkQ2gprGJZyWtwcMTWPoxzLsMmujE9xDeFK4XYMfBdZGh2naMwP3LfbPy/06mazrSs66WRM1oxhz56L2UzTKyCWCl+ld7RlN7aPwfEw2j9VN50YCkMLfQRfIAXTspKQb6o5QQw8ey0cINdtWSHClz/uXiCFqiYJfItxY1rAZkE1Qj0b0izGQFJ9/44Zfw0dJtzCXYgXTPZftPeGTdoX/HnZJpUvfqmLIAdgAyoXi5BxL5bgSs30yaB4bRxJJj7DKpVbAgZmx0ecjmiGDh7t78A16pZ2kz+OIUkuc/hxvUaVehsH1pVdqycpUJfbgy+to0AY/+BBd38GGvv8YdTCa99bSHRGaZuUglLKN/2J0pZmfrIARIdgrV2yDK+IN4hTVKf1jprtfvhvkG+eRyDfoLL9rg8+ZEtdYUdZgDdF3ftmHKTzgxI6leMWX7WFRTHjxVYFVk0yWA9xXk6s/WcG6IFeGYPVF94IcLeC2eAjaMasusF+C6qyFWi6nuyFK2Gr1utvG6kg84Hu0KKYg42MHXIR1AtQW3MWaqosb54y0GutQtnD47l84/PdJvUhuE/a7uyfCjjtyh2sRRLX3WDosyRZsqjLea9EIX6oNmQMZd1WRxM86Ggt6bVOc9KY5Z7HLpLyb0lLF4sdyzfBNJB7u7vqkBzsEss1Yq+sXD0N"
#解密
dec_text = aes.decrypt_oralce(key, enc_msg)
print(key)
print(dec_text)
EDITED:
i got a different result using same IV 16'0'
and
the result from the web is
{"upPacketSN":-1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619687373640,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQ1MDQsMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
while the result of my code is :
KE#`QS[UDc~
1,"upDataSN":-1,"topic":"v1/up/ad","timestamp":1619687373640,"tenantId":"2000034792","serviceId":"","protocol":"lwm2m","productId":"15044315","payload":{"APPdata":"MTIzNDU2NzgsNTM0OCwwNywwOCwyNTUsMTc5LDEyOCwwMDAwLDQ1MDQsMSw2LDAsMCwwLDAsMCwwLDI0LDMuNzAsMy43MCwxLDEsNiwsMCwwLDAsMCwwLDAsMCwwLDAsMCwtMjU1LC0yNTUsLTI1NSwxLjY5LDEuOCw0OTk4LDYwLDE0NDAsNjA="},"messageType":"dataReport","deviceType":"","deviceId":"523ede8bb7e34dd4a1bd74028d63749e","assocAssetId":"","IMSI":"undefined","IMEI":"864162041961023"}
can anyone help me which part i get wrong?
Thanks
Currently the code is not using a "null-IV", which means an array filled with bytes set to zero. Instead it is using an array filled with '0' characters, which have value 0x30 in hexadecimals or 48 in decimals; distinctly not zero.
To create a null-IV please have a look here on how to indicate byte values within byte arrays in Python. The same trick is used within the add_to_16 loop, where the \0 escape is used to indicate a zero byte.
Note that padding a key or IV is very bad practice. Those need to consist of randomized bytes. Beware that working crypto code is not the same thing as secure crypto code, which should probably be your goal.

How to do MCRYPT_RIJNDAEL_256 + MCRYPT_MODE_CBC in Python3?

I have php code, I want to run it in python3 so which Encryption in python equivalent to php's MCRYPT_RIJNDAEL_256?
Here is php code:
<?php
$customerId = 108;
$user = "John Doe";
$password = "mysecretpassword";
$aes_key = "NzQ3MGIyOWEyMDk0MzI3Y2RiNzlkMThjZGY5YTJmY2YzNzI1OTQxMw";
date_default_timezone_set("Asia/Singapore");
// 1)Encode User password in SHA1 : SHA1(User_Password)
$password = sha1($password);
// 2)concat Customer ID, user name, user password encoded in SHA1 and date
$ticket = $customerId.$user.$password.date('Ymd');
// Get padding and AES Initialization Vector
$IVsize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$IV = substr(str_pad($aes_key, $IVsize, $aes_key), 0, $IVsize);
$keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$aes_key = substr(str_pad($aes_key, $keySize, $aes_key), 0, $keySize);
$BlockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$StringLength = strlen($ticket);
$Padding = $BlockSize - ($StringLength % $BlockSize);
// 3)Encrypt the result of 2) in AES 256 (Rijndael) with padding in CBC mode
//:MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC
$ticket .= str_repeat(chr($Padding), $Padding);
$ticket = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $aes_key, $ticket, MCRYPT_MODE_CBC, $IV);
// 4)Encode the result of 3) in base 64
$ticket = base64_encode($ticket);
// 5)In the result of 4), replace reserved characters :
$ticket = strtr($ticket, '+/=', '-_,');
echo "Ticket = '$ticket'";
?>
For mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $aes_key, $ticket, MCRYPT_MODE_CBC, $IV); this function in php which one I need to use in python?
Could anyone help me?
The encryption
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
uses Rijndael with a block size of 256 bits, the CBC mode and Zero padding. Note that mcrypt_encrypt is deprecated.
Rijndael is defined for block and key sizes that correspond to an integer multiple of 32 bits and lie between 128 and 256 bits, here. MCRYPT_RIJNDAEL_256 specifies Rijndael with a block size of 256 bits. Care must be taken here to avoid confusion with AES. AES is a subset of Rijndael with the key sizes 128, 192 and 256 bits and the block size 128 bits. Therefore AES is not used in the posted code.
Unlike AES, Rijndael is not a standard and should therefore be avoided if possible. For this reason Rijndael is not offered in many libraries (unlike AES), for example not in the well-known Python libraries PyCryptodome or Cryptography.
A Python 3 library that implements Rijndael is py3rijndael. The following code shows its use for Rijndael with a block size of 256 bits in CBC mode and Zero padding:
from py3rijndael import RijndaelCbc, ZeroPadding
import base64
key = b'01234567890123456789012345678901'
iv = b'98765432109876543210987654321098'
plaintext = b'The quick brown fox jumps over the lazy dog'
rijndaelCbc = RijndaelCbc(
key = key,
iv = iv,
padding = ZeroPadding(32),
block_size = 32
)
ciphertext = rijndaelCbc.encrypt(plaintext)
print(base64.b64encode(ciphertext).decode('utf8'))
Output:
Ce33Y2Q5YDfven922xt/pZgEFVPWEe6rUuEpMS+YHtq2MPbRZ3L14T4t+EMOoFfoPkGw8cJ7q5oH58vr5hhzFQ==
The result matches the ciphertext provided by mcrypt (assuming the same values for plaintext, key and IV):
$key = '01234567890123456789012345678901';
$iv = '98765432109876543210987654321098';
$plaintext = 'The quick brown fox jumps over the lazy dog';
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
print(base64_encode($ciphertext) . PHP_EOL);
Output:
Ce33Y2Q5YDfven922xt/pZgEFVPWEe6rUuEpMS+YHtq2MPbRZ3L14T4t+EMOoFfoPkGw8cJ7q5oH58vr5hhzFQ==

PyCryptoDome/cryptography inequality with AES-CFB in python

While running a test to make sure that the two different libraries give the same output, I found out that they don't with CFB. Code to replicate the problem is:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from Crypto.Cipher import AES
KEY = b'legoroojlegorooj'
IV = b'legoroojlegorooj'
aes = Cipher(algorithms.AES(KEY), modes.CFB(IV), default_backend()).encryptor()
output_data = aes.update(b'Feathers fall as fast as bowling balls.') + aes.finalize()
del aes
ctr = AES.new(KEY, AES.MODE_CFB, iv=IV)
output_data2 = ctr.encrypt(b'Feathers fall as fast as bowling balls.')
assert output_data == output_data2 # AssertionError
Any help sorting this out would be appreiciated.
This code works with modes.OFB(IV) and AES.MODE_OFB.
In CFB mode the size of the shift register must be specified, whereby the different libraries often use different default values. For distinction, the size of the shift register in bits is often appended to the CFB, i.e. CFB8 uses a shift register of 8 bits and CFB128 a shift register of 128 bits.
Cryptography has the two variants CFB8 and CFB128, where the latter is simply called CFB. PyCryptodome allows the setting in integer multiples of 8 bit by using the parameter segment_size with the default value 8 bit.
So in the current code Cryptography uses CFB128 and PyCryptodome uses CFB8 (its default value), which causes the different results.
The following combinations work:
PyCryptodome with segment_size=128 and Cryptography with CFB. Both correspond to CFB128:
# CFB with a 128 bit shift-register
# Output as hex-string: 63230751cc1efe25b980d9e707396a1a171cd413e6e77f1cd7a2d3deb2217255a36ae9cbf86c66
...
aes = Cipher(algorithms.AES(KEY), modes.CFB(IV), default_backend()).encryptor()
...
ctr = AES.new(KEY, AES.MODE_CFB, iv=IV, segment_size=128)
...
PyCryptodome with segment_size=8 (the default value) and Cryptography with CFB8. Both correspond to CFB8:
# CFB with a 8 bit shift-register
# Output as hex-string: 63d263889ffe94dd4740580067ee798da474c567b8b54319a5022650085c62674628f7c9e790c3
...
aes = Cipher(algorithms.AES(KEY), modes.CFB8(IV), default_backend()).encryptor()
...
ctr = AES.new(KEY, AES.MODE_CFB, iv=IV, segment_size=8)
...
Note, that (1) both Python libraries provide an identical result for the OFB mode, since both use OFB128. (2) CFB128 is faster than CFB8: In CFB8, AES encryption has to be called 16 times for each block vs. 1 time in CFB128.

Pycryptodome can't decrypt data

Why cipher3 can't decrypt cipher data?
cipher2 and cipher3 use same nonce, but cipher3 can't decrypt data
Code:
>>> from Crypto.Cipher import AES
>>> cipher = AES.new(b"M"*16, AES.MODE_EAX)
>>> cipher2 = AES.new(b"M"*16, AES.MODE_EAX, cipher.nonce)
>>> cipher3 = AES.new(b"M"*16, AES.MODE_EAX, cipher.nonce)
>>> data = cipher.encrypt(b"Hello")
>>> data2 = cipher.encrypt(b"World")
>>> cipher2.decrypt(data)
b'Hello'
>>> cipher3.decrypt(data2)
b'S\xa5\x92\xa2\x9a'
>>> cipher2.decrypt(data2)
b'World'
The problem is that cipher objects you use for decryption (cipher2, cipher3 in your case) must be presented the pieces of ciphertext in the same order they were produced (by cipher in your case).
Instead, you are passing data2 as the first piece of ciphertext to cipher3, even though it was produced second.
This is applicable to several other cipher modes, not just EAX.
Note also that EAX is an authenticated cipher mode: you should use the method decrypt_and_verify() unless you have were good reasons not to.

Categories