Skip to content

Instantly share code, notes, and snippets.

@christoph2
Last active April 11, 2024 04:19
Show Gist options
  • Save christoph2/9735c63128e341d68be2c83bb0380d17 to your computer and use it in GitHub Desktop.
Save christoph2/9735c63128e341d68be2c83bb0380d17 to your computer and use it in GitHub Desktop.
Cryptographically secure pseudo-random numbers (Windows only)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import ctypes
from ctypes import wintypes
import struct
PROV_RSA_FULL = 1
class Random(object):
"""
Generate cryptographically secure pseudo-random numbers (Windows only).
"""
# Could/Should be a singleton.
advapi = ctypes.windll.LoadLibrary("advapi32")
genRandom = advapi.CryptGenRandom
genRandom.restype = ctypes.c_bool
genRandom.argtypes = [ctypes.c_void_p, wintypes.DWORD, ctypes.c_char_p]
acquireContext = advapi.CryptAcquireContextW
acquireContext.restype = ctypes.c_bool
acquireContext.argtypes = [ctypes.POINTER(ctypes.c_void_p), wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD]
releaseContext = advapi.CryptReleaseContext
releaseContext.restype = ctypes.c_bool
releaseContext.argtypes = [ctypes.c_void_p, wintypes.DWORD]
def __init__(self):
self.ctx = ctypes.c_void_p()
if not self.acquireContext(ctypes.byref(self.ctx), None, None, PROV_RSA_FULL, 0):
raise RuntimeError("Could not acquire context")
self.buf = ctypes.create_string_buffer(8)
def __del__(self):
if self.ctx.value is not None:
if not self.releaseContext(self.ctx, 0):
raise RuntimeError("Could not release context")
def rand(self, upperBound = None):
"""Returns a 64bit pseudo-random number.
If parameter `upperBound` is given, limit result to `upperBound` (inclusive).
"""
self.genRandom(self.ctx, 8, self.buf)
result = int(struct.unpack('Q', self.buf.raw)[0])
return result % (upperBound + 1) if upperBound else result
##
## Demo code.
##
random = Random()
for i in range(128):
print("{0:08X}".format(random.rand())),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment