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 requests
import random
import secrets
import base64
import os
try: try:
from urllib.parse import urlencode from urllib.parse import urlencode
@ -20,24 +24,92 @@ typeS = {
DEVICE_ID = 'QWR70154' 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 Fetch data from the Randonautica Quantum Random Numbers JSON API.
Falls back to local randomness if the API is unavailable.
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`
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(): if type not in typeS:
raise Exception("type must be one of %s" % typeS.keys()) raise Exception("type must be one of %s" % list(typeS.keys()))
# Try QRNG first
url = URL + typeS[type] + '?' + urlencode({ url = URL + typeS[type] + '?' + urlencode({
'device_id': DEVICE_ID, 'device_id': DEVICE_ID,
'length': length, 'length': length,
}) })
data = __get_json(url) 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 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): def __get_json(url, timeout=1.5):
return requests.get(url, verify=False).json() 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"
}

View file

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="randonautentropy", name="randonautentropy",
version="1.0.2", version="1.0.3",
author="randogoth", author="randogoth",
author_email="randogoth@posteo.org", author_email="randogoth@posteo.org",
description="Python API for interacting with The Randonautica Quantum Random Number Generator", description="Python API for interacting with The Randonautica Quantum Random Number Generator",