how do I use this pycrypto code in pycryptodome:
#!/usr/bin/env python
from Crypto.Cipher import AES
import base64
import os
# the block size for the cipher object; must be 16 per FIPS-197
BLOCK_SIZE = 16
# 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
At the very least, you need to replace:
cipher = AES.new(secret)
with:
cipher = AES.new(secret, AES.MODE_ECB)
PyCryptodome is almost compatible with PyCrypto, with the exceptions of a few dangeours APIs (like leaving ECB as default encryption mode).
Related
I'm writing a test script which I'm using to try and decrypt an encrypted string I have the key to. However, while the code somewhat runs, it is not printing the full string/value that I am expecting (and know the result for).
For example, rather than returning ThisIsTheStringThatWorks it is returning atWorks
Here is the code:
import base64
import hashlib
from Crypto.Cipher import AES
BLOCK_SIZE = 16
unpad = lambda s : s[0:-s[-1]]
def decrypt(enc, secret_key):
private_key = hashlib.sha256(secret_key.encode('utf-8')).digest()
enc = base64.b64decode(enc)
iv = enc[:BLOCK_SIZE]
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[BLOCK_SIZE:]))
decrypted = decrypt(mail_pass, secret_key)
print(bytes.decode(decrypted))
Any help would be greatly appreciated. Thanks in advance.
You have not included the IV in the encrypted message. You are using the first encrypted block as the IV during decryption and as a result the data encrypted in the first block is lost.
You must concatenate the IV and the cipher text in the encryption routine if you want this decryption routine to work:
enc = iv + cipher.encrypt(clear)
I have encrypted some of the data. Here my encryption converter is AES-256-CBC.
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def _pad(self, s):
return s + (AES.block_size- len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
key = some key
raw='한글 (한국말)이라고도하는 한국어는 남한과 한국의 공식 언어입니다'
AESCipher_obj=AESCipher(key)
val = AESCipher_obj.encrypt(raw)
print(val)
When I execute my code it gives me that error.
> Traceback (most recent call last): File "aes2.py", line 27, in
> <module>
> s= AESCipher_obj.encrypt(raw) File "aes2.py", line 17, in encrypt
> return base64.b64encode(iv + cipher.encrypt(raw)) File "/usr/local/lib/python3.5/dist-packages/Crypto/Cipher/blockalgo.py",
> line 244, in encrypt
> return self._cipher.encrypt(plaintext) ValueError: Input strings must be a multiple of 16 in lengt
My input data is in the Korean language but When I try with the English language it is working perfectly. So how to encrypt for a specific language?
Assuming that you use a Python3 version, your string is an unicode string. You should encode it to an utf8 encoded byte string before padding it because the length will change. It works for ascii strings, because the length of the encoded byte string is the length of the unicode string.
So in your code, you must simply do:
key = some key
raw ='한글 (한국말)이라고도하는 한국어는 남한과 한국의 공식 언어입니다'
byte_raw = raw.encode('utf8')
AESCipher_obj=AESCipher(key)
val = AESCipher_obj.encrypt(byte_raw)
print(val)
After decrypting, you will get a byte string that should be decoded with .decode('utf8') to convert it to a Python3 (unicode) string.
Your Error:
ValueError: Input strings must be a multiple of 16 in length
AES is a block cipher, it works on 16-byte (128-bit) blocks.
The data needs to be padded until they're 16 bytes.
See this answer.
Hope this helps you!
try to encode() your string into byte string maybe ? and then padding it.
you can see here https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Cipher/blockalgo.py
that it's working with bytestring instead of unicode (you are using python3)
your step are correct, and I see nothing wrong with the padding mechanism
my goal is to have a very simple AES 128 CBC scheme which encrypts a plaintext and then decrypts it based on a given key in Python. I'm using pycryptodome framework and I couldnt find any documentation with an example of the AES CBC scheme.
Following is my code. The decrypted data is not same as the data before encryption. Will be fantastic if someone can help me identify what is going wrong here.
key = b'Sixteen byte key'
data = 'Jeevan B Manoj'.encode("UTF-8")
data = pad(data,16)
cipher = AES.new(key, AES.MODE_CBC)
print("data before encryption")
print(data)
ciphertext = cipher.encrypt(data)
cipher = AES.new(key, AES.MODE_CBC)
plaintext = cipher.decrypt(ciphertext)
print(plaintext)
As t.m.adam noted, the CBC mode of operation requires an initialization vector (IV) to work. Because the IV is commonly forgotten (also that it has to be unique and unpredictable, e.g. random), Pycryptodome creates a random one when a cipher object is initialized.
The IV must be unique for each encryption and is required for decryption. Common practice (source?) is to put the IV at the start of the ciphertext (the IV does not to need to be secret).
To make your example work:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
# Do not use raw passwords as keys,
# use a derivation functions to generate keys from them
key = b'Sixteen byte key'
data = 'Jeevan B Manoj'.encode("UTF-8")
data = pad(data, AES.block_size)
encryptor = AES.new(key, AES.MODE_CBC)
iv = encryptor.IV
decryptor = AES.new(key, AES.MODE_CBC, IV=iv)
ciphertext = encryptor.encrypt(data)
plaintext = decryptor.decrypt(ciphertext)
assert plaintext == data
Important note: The ciphertext and IV must be authenticated for security (so data cannot be tampered with). For that, Pycryptodome offers AEAD modes like EAX and GCM as pointed out by Hans-Peter Jansen on GitHub. For many of them padding is not required.
If you use MODE_ECB instead of MODE_CBC it works. I also didn't know what padding routine you are using so I used this one.
I have several other examples here:
https://github.com/SolarDon/pycryptodome/tree/master/Examples
from Crypto.Cipher import AES
# Padding for the input string --not related to encryption itself.
BLOCK_SIZE = 16 # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
key = b'Sixteen byte key'
data = 'Jeevan B Manoj'.encode("UTF-8")
data = pad(data)
cipher = AES.new(key, AES.MODE_ECB) # AES.MODE_CBC
print("data before encryption")
print(data)
ciphertext = cipher.encrypt(data)
cipher = AES.new(key, AES.MODE_ECB) # MODE_CBC
plaintext = cipher.decrypt(ciphertext)
print(unpad(plaintext))
I'm trying to encrypt a timestamp using AES-256 and Python with base64. The OpenSSL equivalent of the output is generated with this command:
openssl enc -aes256 -pass pass:'1Lw2*kx18#AvNuij*iRL1nY1UA_#k8$+' -nosalt -base64 <<< "1489355323"
My python code looks like so:
import time
from base64 import b64encode
from Crypto.Cipher import AES
key = '1Lw2*kx18#AvNuij*iRL1nY1UA_#k8$+'
timestamp = "1489355323"
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
iv = "\x00" * 16
aes = AES.new(key, AES.MODE_CBC, iv)
ciphertext = aes.encrypt( pad( timestamp ) )
print b64encode(ciphertext)
Currently the output is different, and I need to get the same output as the OpenSSL command. Any idea what I'm doing wrong?
The key and iv that the OpenSSL enc command use are derived from the password by the EVP_BytesToKey function. You will need to reproduce that function to get your code to behave the same way.
In Python it might look like:
from hashlib import md5
# ...
last = ''
bytes = ''
# 32 byte key (256 bits) + 16 byte IV = 48 bytes needed
while len(bytes) < 48:
last = md5(last + password).digest()
bytes += last
key = bytes[0:32]
iv = bytes[32:48]
# ...
aes = AES.new(key, AES.MODE_CBC, iv)
ciphertext = aes.encrypt( pad( timestamp ) )
This scheme isn’t really recommended anymore, but the enc command still uses it. I believe OpenSSL is looking at providing a more up to date key derivation function in the future.
You also need to take care with newlines. The here string (<<<) adds a newline to the end of the string, you would need to add that to the string you are encrypting to get identical results:
timestamp = "1489355323\n"
Trying to solve Cryptopals Challenge 10 where have to CBC decrypt a text file against "YELLOW SUBMARINE" with an IV of all ASCII 0 (\x00\x00\x00 &c).
Link to the text file is following:
http://cryptopals.com/static/challenge-data/10.txt
I have followed the algorithm CBC uses by taking cipher text, decrypting(using ECB decryption) and then taking xor with Initialization Vector for first block and ciphertext(i-1) for subsequent blocks. However for some not-understandable reason I am not getting a readable decryption. I just see some weird characters when I print after decryption:
from Crypto.Cipher import AES
key ='YELLOW SUBMARINE'
iv = "%00%00%00"*32
iv = iv.replace('%',r'\x')
#XOR-ing function
def xor_strings(a, b):
return "".join(chr(ord(a1) ^ ord(b1)) for a1, b1 in zip(a, b))
#Taking input file and converting it into a single string
file = open('10.txt','r')
data = file.read()
block = 128
obj = AES.new(key, AES.MODE_ECB)
def split_len(string, size):
return [string[i:i+size] for i in range(0, len(string), size)]
mylist = split_len(data,block)
decrypted = ""
for i in range (0,len(mylist)):
mystr = obj.decrypt(mylist[i])
if (i==0):
decrypted = decrypted + xor_strings(mystr,iv)
else:
decrypted = decrypted + xor_strings(mystr, mylist[i-1])
print decrypted
What might be the problem here ?
The iv needs to be 16 zero bytes (the question is not clearly worded here when it says “ASCII 0”):
iv = "\x00" * 16
You need to base64 decode the file before decrypting it:
from base64 import b64decode
#...
file = open('10.txt','r')
data = file.read()
data = b64decode(data)
Finally your block size needs to be in bytes for this code to work, not bits:
block = 16