mpyaes icon indicating copy to clipboard operation
mpyaes copied to clipboard

php openssl compatible

Open csxdotcc opened this issue 1 year ago • 1 comments

can this lib be used in conjunction with phps openssl_encrypt() ?

heres example code i tried but didn't work. php.net

switched to aes-256_ecb so IV woudn't be an issue but still didn't work, does the block size need to be padded?

csxdotcc avatar Nov 15 '24 21:11 csxdotcc

Hi @csxdotcc 👋

I'm unfamiliar with PHP but have gotten the following results.

ECB mode

I used an interactive shell to go through encryption/decryption using AES-128 in ECB mode:

php > $plaintext = "message to be encrypted";
php > $cipher = "aes-128-ecb";
php > $key = openssl_random_pseudo_bytes(openssl_cipher_key_length($cipher));
php > $ciphertext = openssl_encrypt($plaintext, $cipher, $key);
php > print_r($ciphertext);
mRkSDTbqkdLT9Rf2aS6hAIJ1kXXTQ3I0LzX9J14jIP8=
php > $decrypted_ciphertext = openssl_decrypt($ciphertext, $cipher, $key);
php > print_r($decrypted_ciphertext);
message to be encrypted

I got the 16 bytes that make up the key with

php > print_r(implode(", ", unpack("C*", $key)));
254, 14, 140, 121, 110, 130, 46, 134, 30, 88, 114, 215, 217, 119, 56, 164

which I then used on the MicroPython side:

>>> import binascii, mpyaes
>>> key = bytearray([254, 14, 140, 121, 110, 130, 46, 134, 30, 88, 114, 215, 217, 119, 56, 164])
>>> plaintext = "message to be encrypted".encode("utf8")
>>> aes = mpyaes.new(key, mpyaes.MODE_ECB)
>>> aes
<AES 128-bit ECB>
>>> ciphertext = aes.encrypt(plaintext)
>>> ciphertext
bytearray(b"\x99\x19\x12\r6\xea\x91\xd2\xd3\xf5\x17\xf6i.\xa1\x00\x82u\x91u\xd3Cr4/5\xfd'^# \xff")
>>> binascii.b2a_base64(ciphertext, newline=False) # NEWLINE BY DEFAULT!
b'mRkSDTbqkdLT9Rf2aS6hAIJ1kXXTQ3I0LzX9J14jIP8='
>>> decrypted_ciphertext = aes.decrypt(ciphertext)
>>> decrypted_ciphertext
bytearray(b'message to be encrypted')

PHP's openssl_encrypt returns the base64-encoded ciphertext by default, whereas mpyaes returns the raw bytes. If you choose to make use of binascii on the MicroPython side to handle base64 conversions, take care of binascii.b2a_base64, which appends a newline to the base64 string by default. Otherwise you can obtain the raw bytes by passing the OPENSSL_RAW_DATA option when encrypting (and decrypting!), e.g.

php > $ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA);
php > print_r($ciphertext);
6?????i.??u?u?Cr4/5?'^# ?
php > print_r(implode(", ", unpack("C*", $ciphertext)));
153, 25, 18, 13, 54, 234, 145, 210, 211, 245, 23, 246, 105, 46, 161, 0, 130, 117, 145, 117, 211, 67, 114, 52, 47, 53, 253, 39, 94, 35, 32, 255

and on the MicroPython side

>>> list(ciphertext)
[153, 25, 18, 13, 54, 234, 145, 210, 211, 245, 23, 246, 105, 46, 161, 0, 130, 117, 145, 117, 211, 67, 114, 52, 47, 53, 253, 39, 94, 35, 32, 255]

CBC mode

I looked at AES-128 in CBC mode to see how IVs fair. On the PHP side:

php > $cipher = "aes-128-cbc";
php > $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
php > $ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
php > print_r(implode(", ", unpack("C*", $ciphertext)));
30, 107, 132, 168, 161, 158, 77, 152, 168, 73, 39, 156, 38, 3, 180, 192, 216, 99, 197, 44, 116, 125, 156, 4, 252, 216, 140, 65, 212, 5, 241, 16
php > $decrypted_ciphertext = openssl_decrypt($ciphertext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
php > echo $decrypted_ciphertext;
message to be encrypted

The 16 bytes making up the IV:

php > print_r(implode(", ", unpack("C*", $iv)));
14, 29, 232, 142, 145, 57, 46, 97, 15, 248, 74, 117, 226, 38, 207, 230

The MicroPython side:

>>> iv = bytearray([14, 29, 232, 142, 145, 57, 46, 97, 15, 248, 74, 117, 226, 38, 207, 230])
>>> aes = mpyaes.new(key, mpyaes.MODE_CBC, IV=iv)
>>> aes
<AES 128-bit CBC>
>>> ciphertext = aes.encrypt(plaintext)
>>> list(ciphertext)
[30, 107, 132, 168, 161, 158, 77, 152, 168, 73, 39, 156, 38, 3, 180, 192, 216, 99, 197, 44, 116, 125, 156, 4, 252, 216, 140, 65, 212, 5, 241, 16]
>>> decrypted_ciphertext = aes.decrypt(ciphertext)
>>> decrypted_ciphertext
bytearray(b'message to be encrypted')

Without exhausting AES cipher parameter combinations, to my understanding mpyaes seems compatible with PHP's OpenSSL extension. What was it you tried that didn't work?

iyassou avatar Jan 14 '25 02:01 iyassou