Python SDK for the QCicada QRNG (Crypta Labs) — macOS-first, works on Linux too
Project description
qcicada
Rust and Python SDK for the QCicada quantum random number generator by Crypta Labs.
The official pyqcc SDK doesn't work on macOS due to FTDI serial driver differences. This SDK fixes that — and provides a cleaner API with no monkey-patching.
Quick Start
Python
pip install ./python # requires pyserial
from qcicada import QCicada
with QCicada() as qrng:
print(qrng.random(32).hex())
Rust
[dependencies]
qcicada = "0.1"
use qcicada::QCicada;
let mut qrng = QCicada::open(None, None)?;
let bytes = qrng.random(32)?;
That's it. The device is auto-detected. If you have multiple USB-serial devices, pass the port explicitly: QCicada(port="/dev/cu.usbserial-DK0HFP4T").
Finding Your Device
from qcicada import find_devices, discover_devices, open_by_serial
# List USB-serial ports (fast, no device communication)
find_devices()
# ['/dev/cu.usbserial-DK0HFP4T']
# Probe each port and confirm it's actually a QCicada
for dev in discover_devices():
print(f"{dev.port} serial={dev.info.serial} hw={dev.info.hw_info}")
# /dev/cu.usbserial-DK0HFP4T serial=QC0000000217 hw=CICADA-QRNG-1.1
# Open a specific device by serial number
qrng = open_by_serial("QC0000000217")
The same functions are available in Rust.
Entropy Modes
The QCicada supports three post-processing modes:
| Mode | What you get |
|---|---|
| SHA256 (default) | NIST SP 800-90B conditioned output — use this for cryptography |
| Raw Noise | Noise after health-test conditioning — use this for entropy research |
| Raw Samples | Unprocessed samples from the quantum optical module |
from qcicada import QCicada, PostProcess
with QCicada() as qrng:
# Default: SHA256
qrng.random(32).hex()
# Switch to raw noise
qrng.set_postprocess(PostProcess.RAW_NOISE)
qrng.random(32).hex()
# Switch to raw samples
qrng.set_postprocess(PostProcess.RAW_SAMPLES)
qrng.random(32).hex()
Device Info & Status
with QCicada() as qrng:
info = qrng.get_info()
# DeviceInfo(serial='QC0000000217', fw_version=0x5000e,
# core_version=0x1000c, hw_info='CICADA-QRNG-1.1')
status = qrng.get_status()
# DeviceStatus(initialized=True, ready_bytes=13440, ...)
stats = qrng.get_statistics()
# DeviceStatistics(generated_bytes=4928, speed=100696, ...)
config = qrng.get_config()
# DeviceConfig(postprocess=SHA256, auto_calibration=True, block_size=448, ...)
Full Configuration
Every device setting is readable and writable:
from dataclasses import replace
config = qrng.get_config()
# Modify and write back
config = replace(config, block_size=256, auto_calibration=False)
qrng.set_config(config)
| Field | Type | Description |
|---|---|---|
postprocess |
PostProcess |
SHA256, RawNoise, or RawSamples |
initial_level |
float |
LED initial level |
startup_test |
bool |
Run health test on startup |
auto_calibration |
bool |
Auto-calibrate light source |
repetition_count |
bool |
NIST SP 800-90B repetition count test |
adaptive_proportion |
bool |
NIST SP 800-90B adaptive proportion test |
bit_count |
bool |
Crypta Labs bit balance test |
generate_on_error |
bool |
Keep generating if a health test fails |
n_lsbits |
int |
Number of LSBs to extract per sample |
hash_input_size |
int |
Bytes fed into SHA256 per output block |
block_size |
int |
Output block size in bytes |
autocalibration_target |
int |
Target value for auto-calibration |
Signed Reads
Get random bytes with a cryptographic signature (requires firmware 5.13+):
result = qrng.signed_read(32)
print(result.data.hex()) # 32 random bytes
print(result.signature.hex()) # 64-byte signature
The signature is produced by the device's internal asymmetric key. Check the QCicada documentation for how to extract the public key and verify signatures.
Continuous Mode
For high-throughput streaming, use continuous mode instead of one-shot:
with QCicada() as qrng:
qrng.start_continuous()
for _ in range(100):
chunk = qrng.read_continuous(1024)
process(chunk)
qrng.stop()
The device streams random data until stop() is called. There's no per-request overhead, so this is faster for bulk entropy collection.
API Reference
| Method | Description |
|---|---|
random(n) |
Get n random bytes (1–65535, one-shot) |
signed_read(n) |
Get n random bytes + 64-byte signature (FW 5.13+) |
start_continuous() |
Start continuous streaming mode |
read_continuous(n) |
Read n bytes from continuous stream |
fill_bytes(buf) |
Fill a buffer of any size (auto-chunks) |
get_info() |
Serial number, firmware version, hardware |
get_status() |
Health flags, ready byte count |
get_config() |
Full device configuration |
set_config(config) |
Write device configuration |
set_postprocess(mode) |
Shortcut to change entropy mode |
get_statistics() |
Bytes generated, speed, failure counts |
reset() |
Restart generation and clear statistics |
stop() |
Halt any active generation |
close() |
Close serial port |
Rust: QCicada also implements std::io::Read, so it works anywhere a reader is expected.
Why Not pyqcc?
The official Crypta Labs SDK (pyqcc) has macOS issues:
- Uses
/dev/tty.*ports — macOS needs/dev/cu.* - Sets
inter_byte_timeout— causes FTDI read failures on macOS - Timeouts too short — macOS FTDI driver needs at least 500ms
- No flush delay — FTDI driver drops bytes without a post-write pause
- Device may be left in continuous mode — no drain on connect
This SDK fixes all of these. It also works fine on Linux.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qcicada-0.1.1.tar.gz.
File metadata
- Download URL: qcicada-0.1.1.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f01bed1ab5a75492fc3f2339ef6e35c8f6c72afcd06219344a406d89ba28f3ad
|
|
| MD5 |
02910138d692689766d31beca5e1b997
|
|
| BLAKE2b-256 |
9f7f457f1c7e07e1fa4f1525dce2cc0f0603647a60a43c19df4379428c5e9f2a
|
File details
Details for the file qcicada-0.1.1-py3-none-any.whl.
File metadata
- Download URL: qcicada-0.1.1-py3-none-any.whl
- Upload date:
- Size: 12.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7260cc6cf6d5dee1e8a25f1dbef80e86c5d634b9fe29df95ec5d8f06af24ad4f
|
|
| MD5 |
e9d6f5dfe47f8a8d077352131f198b85
|
|
| BLAKE2b-256 |
01bc23a62bac2604b49e6b9c324ba18a063ce548ae97564b254a4131699054ee
|