#!/usr/bin/env python3 import sys from typing import Optional from cffi import FFI class RandError(RuntimeError): pass ffi = FFI() ffi.cdef(''' void RAND_seed(const void *buf, int num); void RAND_add(const void *buf, int num, double entropy); int RAND_status(void); int RAND_event(unsigned int iMsg, void* wParam, void* lParam); void RAND_screen(void); int RAND_bytes(unsigned char *buf, int num); int RAND_pseudo_bytes(unsigned char *buf, int num); const char *ERR_reason_error_string(unsigned long); unsigned long ERR_get_error(void); ''') lib = ffi.dlopen('crypto') def rand_add(buf: bytes, num: int, entropy: float) -> None: assert isinstance(entropy, float) lib.RAND_add(buf, num, entropy) def rand_seed(buf: bytes, num: int) -> None: lib.RAND_seed(buf, num) def rand_status() -> int: return lib.RAND_status() def rand_event(iMsg: int, wParam: int, lParam: int) -> Optional[int]: if sys.platform == 'win32': return lib.RAND_event(iMsg, wParam, lParam) else: return None def rand_screen() -> None: if sys.platform == 'win32': lib.RAND_screen() def rand_bytes(n: int = 8) -> bytes: buf = ffi.new('unsigned char buf[{:d}]'.format(n)) ret = lib.RAND_bytes(buf, n) if ret == 1: return bytes(buf) elif ret == 0: RandError("Not enough randomness.") elif ret == -1: RandError("Not supported by the current RAND method.") else: err_msg = lib.ERR_reason_error_string(lib.ERR_get_error()) if err_msg is not None: RandError(err_msg) else: RandError("Unknown error in function RAND_bytes") def rand_pseudo_bytes(n: int) -> bytes: blob = ffi.new('unsigned char buf[{:d}]'.format(n)) ret = lib.RAND_pseudo_bytes(blob, n) if ret == -1: RandError("Not supported by the current RAND method.") elif ret in [0, 1]: return (bytes(blob), ret) else: RandError("Unknown error in RAND_pseudo_bytes")