Files
identity-micropython-tag/identity_iot_aes.py

65 lines
2.0 KiB
Python

# ============================================================+
# Identity IoT — AES-CBC ITEMS Envelope
# (c) Copyright : ae Aeonian Engineering Limited - Hong Kong
# (c) Copyright : WIDE di D. Papa - Naples - Italy
# ============================================================+
# AES-CBC encryption/decryption compatible with PHP ITEMSEncrypter.
# Uses ucryptolib (built-in on Pico W / ESP32).
# ============================================================+
import ucryptolib
import ubinascii
def _pkcs7_pad(data: bytes, block_size: int = 16) -> bytes:
pad_len = block_size - (len(data) % block_size)
return data + bytes([pad_len] * pad_len)
def _pkcs7_unpad(data: bytes) -> bytes:
pad_len = data[-1]
return data[:-pad_len]
def aes_cbc_encrypt(plaintext: str, key: bytes, iv: bytes) -> str:
"""
Encrypt plaintext string with AES-CBC.
Returns base64-encoded ciphertext.
Compatible with PHP ITEMSEncrypter::encryptAES().
"""
if isinstance(key, str):
key = key.encode()
if isinstance(iv, str):
iv = iv.encode()
if isinstance(plaintext, str):
plaintext = plaintext.encode('utf-8')
padded = _pkcs7_pad(plaintext)
cipher = ucryptolib.aes(key, 2, iv) # mode 2 = CBC
encrypted = cipher.encrypt(padded)
return ubinascii.b2a_base64(encrypted).decode().strip()
def aes_cbc_decrypt(ciphertext_b64: str, key: bytes, iv: bytes) -> str:
"""
Decrypt base64-encoded AES-CBC ciphertext.
Returns plaintext string.
Compatible with PHP ITEMSEncrypter::decryptAES().
"""
if isinstance(key, str):
key = key.encode()
if isinstance(iv, str):
iv = iv.encode()
# Normalize base64
s = ciphertext_b64.strip().replace('\n', '').replace('\r', '')
s = s.replace('-', '+').replace('_', '/')
pad = len(s) % 4
if pad:
s += '=' * (4 - pad)
encrypted = ubinascii.a2b_base64(s)
cipher = ucryptolib.aes(key, 2, iv) # mode 2 = CBC
decrypted = cipher.decrypt(encrypted)
return _pkcs7_unpad(decrypted).decode('utf-8')