pseudo fallback
This commit is contained in:
parent
ca812c06c5
commit
5bcfd63d11
2 changed files with 88 additions and 16 deletions
|
|
@ -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"
|
||||||
|
}
|
||||||
2
setup.py
2
setup.py
|
|
@ -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",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue