-1
$\begingroup$

I'm trying to understand the difference in output of AES decrypt (GCM mode) using Webcrypto and Cryptodome.Cipher.AES in python.

from Cryptodome.Cipher import AES
from binascii import hexlify, unhexlify
key = 'E52BC6CEDC2E096584B1F57D3AE6A60724C7B301EECB975813819673E6D23126'
iv = 'B5DDEDB80480A649D1763BE7'
encrypted_data = '37631B44E6B7DB7C404F2947F40FC629BB537BF303B728A674280C23528C3B09815BA2CD39B3844A38C4527464353A66'
cipher_item_key = AES.new(unhexlify(key), AES.MODE_GCM, nonce=unhexlify(iv))
res = cipher_item_key.decrypt(unhexlify(encrypted_data))
print hexlify(res).upper()

prints EEE72939E910F74104846B5BADBDDAD007A46E3392ADE713F6EE33900299C250C83D25005D7D7E27211B2BBFF48E62AC

vs

https://gist.github.com/syst3mw0rm/9b22a1eadd6cbf38474573d54cd54f0c

prints EEE72939E910F74104846B5BADBDDAD007A46E3392ADE713F6EE33900299C250

I noticed that the first 64 characters as exactly same and that makes me think that the issue is probably with unpadding of output in python but I'm not sure what's the right way to go about doing it or is that is the root cause in the first place.


Similarly, I'm noticing a difference in output when encrypting data.

from Cryptodome.Cipher import AES
from binascii import hexlify, unhexlify
key = 'EEE72939E910F74104846B5BADBDDAD007A46E3392ADE713F6EE33900299C250'
iv = '523AEBE425EA83DCE42F9512'
data = '{"username":"test","password":"test"}'
cipher_data = AES.new(unhexlify(key), AES.MODE_GCM, nonce=unhexlify(iv))
cipher_data.update('')
encrypted_data, mac = cipher_data.encrypt_and_digest(bytearray(data))
print (hexlify(encrypted_data) + hexlify(mac)).upper()

prints C7D1A799120CBDD6539378509AFC4D0C3F78BA8D42C9F3546E15DFBD94CBE789350DD3554AF092A39AF0345623E117D28F493CD7DE

vs

https://gist.github.com/syst3mw0rm/22bb4a8c7c51516b358b482b3336d927

prints C7F3F0EA027EA0B75BF6286AD688497F265AF3AF10A8BA273B7AD9D9D3F1B6FD247E85771BAE4447F81447389A84A3145155E7DE387D6B3E181516707731EC2DC3CEF847FCA949EE484B3FC0A6C66853D42C956BA0735642D29E

I'm not sure why the output of above two would be any different. I'm using exact same key, iv, additionalData (defaults to '' according to subtle crypto spec), data. Can someone please tell me if I'm missing something obvious?

$\endgroup$
  • $\begingroup$ AES-GCM -- gist.github.com/syst3mw0rm/… $\endgroup$ – aamir Oct 25 '18 at 10:05
  • $\begingroup$ I'm actually trying to decrypt. The value of iv, key and encrypted data is exactly same in both scenarios. $\endgroup$ – aamir Oct 25 '18 at 10:19
  • $\begingroup$ Did you try to first encrypt something and see if it generates the same ciphertext? "Known-answer tests" tend to help identify the issue. If you would have a value for which you know what it should decrypt to you might see what the issue is. $\endgroup$ – mephisto Oct 28 '18 at 11:28
3
$\begingroup$

The method decrypt takes two slightly different meanings:

  • In WebCrypto, decrypt is a once-off operation that performs decryption and verification of the MAC tag. The tag is assumed to be the last 16 bytes of the input. You can call decrypt only once. In some APIs, this method is (better) called unseal or unbox, to highlight that it does more than decrypting data.
  • In PyCryptodome, decrypt is a streaming operation that decrypts all the data you provide. You can call decrypt multiple times (hence, it is useful in a streaming context where you process a chunk at a time). There is a separate verify method to check the MAC tag at the very end. The once-off method decrypt_and_verify is the equivalent of decrypt in WebCrypto world.

You should therefore modify your Python code:

res = cipher_item_key.decrypt(unhexlify(encrypted_data))

To be:

raw = unhexlify(encrypted_data)
res = cipher_item_key.decrypt_and_verify(raw[:-16], raw[-16:])

Similarly for encryption:

  • In WebCrypto, encrypt performs two operations: actual encryption but also creation of the MAC tag. The output consists of ciphertext and the 16-byte tag, concatenated. You can call encrypt only once.
  • In PyCryptodome, encrypt only performs encryption and you must call digest at the very end (possibly after multiple encrypt calls) to get the 16-byte MAC tag. The method that performs encryption and creation of the MAC tag is actually encrypt_and_digest (same as decrypt in WebCrypto).

It is also helpful to look at the state machine of an AEAD modes (like GCM is) in PyCryptodome:

enter image description here

$\endgroup$
  • $\begingroup$ Can you also please answer the encryption part. I added it as a separate question earlier but it got marked as duplicate and I've merged it into this question now. $\endgroup$ – aamir Oct 28 '18 at 21:43
  • $\begingroup$ I'm facing a different issue with encryption. I'm already using encrypt_and_digest and the output does not match at all. $\endgroup$ – aamir Oct 29 '18 at 14:22
  • 1
    $\begingroup$ That's because you are not encrypting the same data. In the JS case, your string gets encoded as UTF-16, in the Python case it's ASCII. $\endgroup$ – SquareRootOfTwentyThree Oct 29 '18 at 22:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.