pseudo fallback

This commit is contained in:
randogoth 2025-09-14 14:00:55 +03:00
parent ca812c06c5
commit 5bcfd63d11
2 changed files with 88 additions and 16 deletions

View file

@ -1,4 +1,8 @@
import requests
import random
import secrets
import base64
import os
try:
from urllib.parse import urlencode
@ -11,33 +15,101 @@ requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
URL = 'https://qrng.randonautica.com/api/json/'
typeS = {
'int32' : 'randint32',
'hex16' : 'randhex',
'uniform' : 'randuniform',
'int32' : 'randint32',
'hex16' : 'randhex',
'uniform': 'randuniform',
'normal' : 'randnormal',
'base64' : 'randbase64'
}
DEVICE_ID = 'QWR70154'
def get(length=10, type='hex16'):
def get(length=10, type='hex16', timeout=1.5):
"""
Fetch data from the Randonautica Quantum Random Numbers JSON API
length (int): length of bytes to get from the QRNG when `type=hex16`
type (string): type of random number data to fetch.
Valid choices are `hex16`, `int32`, `uniform`, `normal`, `base64`
Fetch data from the Randonautica Quantum Random Numbers JSON API.
Falls back to local randomness if the API is unavailable.
length (int): number of values/bytes to get
type (str): one of {'hex16','int32','uniform','normal','base64'}
timeout (float): HTTP timeout in seconds
"""
if type not in typeS.keys():
raise Exception("type must be one of %s" % typeS.keys())
if type not in typeS:
raise Exception("type must be one of %s" % list(typeS.keys()))
# Try QRNG first
url = URL + typeS[type] + '?' + urlencode({
'device_id': DEVICE_ID,
'length': length,
})
data = __get_json(url)
return data
try:
data = __get_json(url, timeout=timeout)
# Validate expected shape minimally
if isinstance(data, dict) and "data" in data and isinstance(data["data"], list):
return data
# If shape unexpected, fall back
except Exception:
pass
# Fallback path: generate locally with best available primitives
return _fallback_response(type=type, length=length)
def __get_json(url):
return requests.get(url, verify=False).json()
def __get_json(url, timeout=1.5):
resp = requests.get(url, verify=False, timeout=timeout)
resp.raise_for_status()
return resp.json()
def _fallback_response(type: str, length: int):
"""
Produce a response object that mirrors the QRNG API:
{
"type": <type>,
"length": <length>,
"data": [...],
"success": true,
"source": "fallback"
}
"""
if type == 'uniform':
# Uniform floats in [0.0, 1.0)
data = [random.random() for _ in range(length)]
elif type == 'normal':
# Standard normal N(0,1)
data = [random.gauss(0.0, 1.0) for _ in range(length)]
elif type == 'int32':
# 32-bit signed ints (match typical randint32 range: [-2^31, 2^31-1])
# secrets is preferable to random for stronger entropy.
data = []
for _ in range(length):
u = secrets.randbits(32)
# Convert to signed 32-bit
if u & (1 << 31):
u = u - (1 << 32)
data.append(u)
elif type == 'hex16':
# Hex string of length*2 bytes? The API name suggests 16-bit hex bytes.
# Historically this endpoint returns hex bytes; emulate by returning a single hex string
# of 2*length characters (each byte -> 2 hex chars). If API returns list, adapt as needed.
# Here we return a list of hex byte strings to match "data": [...]
data = [secrets.token_hex(1) for _ in range(length)]
elif type == 'base64':
# Return base64-encoded random bytes; match as a list of base64 strings
data = []
for _ in range(length):
b = os.urandom(16) # 16 bytes per element; adjust if your API uses a different size
data.append(base64.b64encode(b).decode('ascii'))
else:
# Should never hit due to earlier check
data = []
return {
"type": type,
"length": length,
"data": data,
"success": True,
"source": "pseudo"
}