Skip to content

Instantly share code, notes, and snippets.

@jaraco
Last active August 10, 2023 14:32
Show Gist options
  • Save jaraco/e7774eb23b4f4aeb5095ee02e6bf6a3e to your computer and use it in GitHub Desktop.
Save jaraco/e7774eb23b4f4aeb5095ee02e6bf6a3e to your computer and use it in GitHub Desktop.
import ctypes
from ctypes import c_void_p, c_int, c_char, c_ulong
lib_path = '/opt/homebrew/opt/openssl@3/lib/libcrypto.dylib'
lib = ctypes.cdll.LoadLibrary(lib_path)
MAX_BLOCK_LENGTH = 32
MAX_IV_LENGTH = 16
class CipherType(ctypes.Structure):
_fields_ = [
('nid', c_int),
('block_size', c_int),
('key_len', c_int),
('iv_len', c_int),
('flags', c_ulong),
('init', c_void_p),
('do_cipher', c_void_p),
('cleanup', c_void_p),
('ctx_size', c_int),
('set_asn1_parameters', c_void_p),
('get_asn1_parameters', c_void_p),
('ctrl', c_void_p),
('app_data', c_void_p),
]
class Cipher(ctypes.Structure):
_fields_ = [
('cipher', c_void_p), # POINTER(CipherType)
('engine', c_void_p), # POINTER(ENGINE)
('encrypt', c_int),
('buf_len', c_int),
('oiv', c_char * MAX_IV_LENGTH),
('iv', c_char * MAX_IV_LENGTH),
('buf', c_char * MAX_BLOCK_LENGTH),
('num', c_int),
('app_data', c_void_p),
('key_len', c_int),
('flags', c_ulong),
('cipher_data', c_void_p),
('final_used', c_int),
('block_mask', c_int),
('final', c_char * MAX_BLOCK_LENGTH),
]
def get_error():
err = lib.ERR_get_error()
msg = ctypes.create_string_buffer(1024)
lib.ERR_error_string(err, msg)
return msg.value.decode()
lib.EVP_CIPHER_CTX_new.restype = ctypes.POINTER(Cipher)
ctx = lib.EVP_CIPHER_CTX_new().contents
assert ctx
key = '11111111111111111111111111111111'.encode()
iv = '1111111111111111'.encode()
engine = None
lib.EVP_get_cipherbyname.argtypes = (ctypes.c_char_p,)
lib.EVP_get_cipherbyname.restype = ctypes.POINTER(CipherType)
cipher = lib.EVP_get_cipherbyname('AES-256-CBC'.encode('ascii'))
assert cipher
encrypt = True
lib.EVP_CipherInit_ex.argtypes = (
ctypes.POINTER(Cipher),
ctypes.POINTER(CipherType),
ctypes.c_void_p,
ctypes.c_char_p,
ctypes.c_char_p,
)
res = lib.EVP_CipherInit_ex(ctx, cipher.contents, engine, key, iv, encrypt)
if res == 0:
print(f"Error initializing cipher: {get_error()}")
raise SystemExit(1)
data = b''
out = ctypes.create_string_buffer(len(data) + MAX_BLOCK_LENGTH - 1)
out_len = ctypes.c_int()
out_data = []
lib.EVP_CipherUpdate.argtypes = (
ctypes.POINTER(Cipher),
ctypes.c_char_p,
ctypes.POINTER(ctypes.c_int),
ctypes.c_char_p,
ctypes.c_int,
)
res = lib.EVP_CipherUpdate(ctx, out, out_len, data, len(data))
if res != 1:
print(f"Error updating cipher: {get_error()}")
raise SystemExit(1)
out_data.append(out.raw[: out_len.value])
out = ctypes.create_string_buffer(MAX_BLOCK_LENGTH)
out_len = ctypes.c_int()
lib.EVP_CipherFinal_ex.argtypes = (
ctypes.POINTER(Cipher),
ctypes.c_char_p,
ctypes.POINTER(ctypes.c_int),
)
res = lib.EVP_CipherFinal_ex(ctx, out, out_len)
if res != 1:
print(f"Error finalizing cipher: {get_error()}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment