summaryrefslogtreecommitdiffstats
path: root/rand.py
blob: 285eb9833415656018e7980b8f83d55a175c4731 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/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")