0

I am using the code presented in the documentation of the PyCryptoDome AES CBC documentation.

import json
from base64 import b64encode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes

data = b"secret"
key = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(data, AES.block_size))
iv = b64encode(cipher.iv).decode('utf-8')
ct = b64encode(ct_bytes).decode('utf-8')
result = json.dumps({'iv':iv, 'ciphertext':ct})
print(result)

The expected output is:

{"iv": "bWRHdzkzVDFJbWNBY0EwSmQ1UXFuQT09", "ciphertext": "VDdxQVo3TFFCbXIzcGpYa1lJbFFZQT09"}

But the output I am getting (Same copy-pasted code is):

{"iv": "PnMwNa/dPBGSkg5LeJ++1g==", "ciphertext": "OGTvHSp2O4dGgfbKbXpSzA=="}

I thought the issue might be caused by the random key, so I run it 20 times in a loop:

import json
from base64 import b64encode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes

print('Result should be like\n{"iv": "bWRHdzkzVDFJbWNBY0EwSmQ1UXFuQT09", "ciphertext": "VDdxQVo3TFFCbXIzcGpYa1lJbFFZQT09"}')

for x in range(0, 20):
    data = b"secret"
    key = get_random_bytes(16)
    cipher = AES.new(key, AES.MODE_CBC)
    ct_bytes = cipher.encrypt(pad(data, AES.block_size))
    iv = b64encode(cipher.iv).decode('utf-8')
    ct = b64encode(ct_bytes).decode('utf-8')
    result = json.dumps({'iv':iv, 'ciphertext':ct})
    print(result)

Sadly, all the results were similar:

Result should be like
{"iv": "bWRHdzkzVDFJbWNBY0EwSmQ1UXFuQT09", "ciphertext": "VDdxQVo3TFFCbXIzcGpYa1lJbFFZQT09"}
{"iv": "PnMwNa/dPBGSkg5LeJ++1g==", "ciphertext": "OGTvHSp2O4dGgfbKbXpSzA=="}
{"iv": "FR9GQqA3tssPKn2YHhHe4w==", "ciphertext": "FM3CLY51i0Gvctr9efXGDg=="}
{"iv": "ocsVcOggvjUXj8s2IHXbUg==", "ciphertext": "mR3mYEK4M3QhtUmPt6jwuA=="}
{"iv": "TCox/Pfv5Xjkrm8z3weJ0w==", "ciphertext": "DkkGKXvXpV4B8pV5B4ctxw=="}
{"iv": "eZvCrk97EC4eL1kO5PQ+Dg==", "ciphertext": "idaLj7wl/F79qUdzZodbBg=="}
{"iv": "KqwaWZTxU5ZFn6Ekq/wWEg==", "ciphertext": "A25LP0wNTJRkndhvuhuasw=="}
{"iv": "FlFNCnQHmUajP2jNDvIiLw==", "ciphertext": "T9vOu0z9DkUrXCUGqXacxw=="}
{"iv": "U74vTvWZNhKbp5sq9uT9Sg==", "ciphertext": "vfJ1Brftb897O6xTMI3MQg=="}
{"iv": "tIVgKly8wyhpwdoRQ6SzqA==", "ciphertext": "EjnyNuIPVmt7cnEaFSsoww=="}
{"iv": "5qoVwEB7WfHGSGZvHY25Hg==", "ciphertext": "RBxS7gIMAfZqS5a9vTEcjQ=="}
{"iv": "NCHdCr09s9pMQsHK+4ECjA==", "ciphertext": "V8ezqaw8WrrygrokrD48ew=="}
{"iv": "x+T+fM3diahDBie4o7fvpA==", "ciphertext": "0xnjaIArYsfKn5JkjXnniQ=="}
{"iv": "v0r3123VCkZy7zOVixyBkA==", "ciphertext": "J2U0MEHqDEwC45UCY7+SdA=="}
{"iv": "l1iA//cLxESUax39ZanyRw==", "ciphertext": "QLC74BydGuQ1J73PUvKl+Q=="}
{"iv": "FcsfeZ3iCrVVaRsZSm3Gsw==", "ciphertext": "wyiZEo1VpdM/u+ucstzn6Q=="}
{"iv": "av6FSkAzfkOyfLvxDIHY9g==", "ciphertext": "EuexPtCb69+37F9INqWemw=="}
{"iv": "a8F+8OEv1ieHcgwMXocEww==", "ciphertext": "6rd04diPiA7tw/4MgIJsWw=="}
{"iv": "jUY115jhERX2KI+dtVajlQ==", "ciphertext": "TRC5RR3qX6EDjjH1kmO6ew=="}
{"iv": "fcW66kw4gpEWRvNf3AQg7g==", "ciphertext": "o1G6luiDT1Cf2bmZlTu57A=="}
{"iv": "U5FbVnDONa0h4Q6PDJo60A==", "ciphertext": "ilGJKuKNxUxx/Lxz5N85NQ=="}

So, my question is, what could be possibly causing this mismatch? Also, please note that the IV size is wrong compared to the expected output

BCT
  • 193
  • 8
  • As long as you do not specify an IV, a random IV will be generated (see the PyCryptodome doc). A random IV and/or a random key will produce different ciphertexts for the same plaintext. In this sense, `{"iv": "bWRH...", "ciphertext": "VDdx..."}` is not the _expected_ result, but a _possible_ result. However, the IV in the posted example / PyCryptodome doc has the wrong size (a bug in the doc). For AES, the IV is 16 bytes long or 24 bytes Base64 encoded. I.e. the code works correctly. – Topaco May 22 '21 at 16:08
  • For AES encryption in CBC mode it is essential that the same IV must never be reused. To prevent inexperienced user to do so the PyCryptoDome function is using a randomly generated IV - that is what you get as output for each round. Second: You didn't explain where your "expected" IV is coming from - it is a Base64 encoded value of a (**already**) Base64 encoded output. When Base64 decoding your expected value you get "mdGw93T1ImcAcA0Jd5QqnA==" that decoded perfectly to 16 byte long IV (in hex string encoding 99d1b0f774f5226700700d0977942a9c) so everything is fine with your function. – Michael Fehr May 22 '21 at 16:09
  • Also the ciphertext `VDdx...` has the wrong size. IV and ciphertext are _double_ Base64 encoded. But the code (as your tests show) outputs IV and ciphertext only _single_ Base64 encoded. So the posted output is not consistent with the code. As said, a bug in the documentation. – Topaco May 22 '21 at 16:23
  • @Topaco you said there was a bug in the doc (wrong size), you also said that the code works correctly, do you have an example I can run with an expected output size? Also by expected output I meant expected output size follow the doc instructions. – BCT May 22 '21 at 16:41
  • Or is there another library you suggest I should use? – BCT May 22 '21 at 16:43
  • The code and your output, e.g. `{"iv": "PnMw...", "ciphertext": "OGTv..."}` are **correct**, the IV and the ciphertext (for the plaintext `secret`) are 16 bytes long and 24 bytes Base64 encoded respectively. Only the example posted in the documentation `{"iv": "bWRH...", "ciphertext": "VDdx..."}` is wrong and contains the _double_ instead of the _single_ base64 encoded values. There is no reason to change the library because of this. – Topaco May 22 '21 at 16:51
  • assuming the code works, I ran it and got these results `Key b'\xe6\xc8\xe5\x1bn\xa0)\x15\x1eU7\x8fk\xd8\xb0lJ\x83T\x02\x89j\xa0\xa9' {"iv": "tSS7DkPNk3u327048hYr9w==", "ciphertext": "i71oZoTk7aKxckhcbGCSEg=="}` any advice on how I can decode these in an online tool? (such as this one for example https://www.devglan.com/online-tools/aes-encryption-decryption) – BCT May 22 '21 at 17:16
  • It is easier if you output the key hex encoded (e.g. with `hex()`): `e6c8e51b6ea029151e55378f6bd8b06c4a835402896aa0a9`. A suitable online tool is [CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)AES_Decrypt(%7B'option':'Hex','string':'e6c8e51b6ea029151e55378f6bd8b06c4a835402896aa0a9'%7D,%7B'option':'Base64','string':'tSS7DkPNk3u327048hYr9w%3D%3D'%7D,'CBC','Raw','Raw',%7B'option':'Hex','string':''%7D,%7B'option':'Hex','string':''%7D)&input=aTcxb1pvVGs3YUt4Y2toY2JHQ1NFZz09). – Topaco May 22 '21 at 19:07
  • Note that the key has 24 bytes (AES-192), i.e. you can't have used the above code because it applies a 16 bytes key (AES-128). – Topaco May 22 '21 at 19:08

1 Answers1

0

It turns out the code is working and the documentation has something wrong as the output, remember when trying to decode to decode the IV and the cipher into HEX

BCT
  • 193
  • 8