Shorkin
A quantum computing toolkit for Python. Implements Shor's algorithm for integer factorization, Deutsch-Jozsa for constant/balanced classification, Grover's algorithm for unstructured search, quantum teleportation, and Quantum Key Distribution (QKD) protocols with HTTP and gRPC transport integrations.
Quantum simulation is powered by Google Cirq. QKD-derived keys are used for AES-256-GCM payload encryption.
Installation
# Core library (Shor's algorithm + QKD protocols)
pip install shorkin
# With a specific web framework
pip install shorkin[fastapi]
pip install shorkin[django]
pip install shorkin[starlette]
pip install shorkin[tornado]
# With gRPC support
pip install shorkin[grpc]
# Everything
pip install shorkin[all]
Requires Python 3.10+.
Shor's Algorithm
Factor composite integers using a quantum (simulated via Cirq) or classical order-finding backend.
CLI
# Factor an integer
shorkin factor 15
# 15 = 3 x 5
# Factor with a specific mode
shorkin factor 21 --mode classical
shorkin factor 15 --mode quantum --verbose
# Factor the modulus from an RSA public key
shorkin factor-key path/to/public.pem
Library
from shorkin.factor import factor
from shorkin.classical.order_finder import find_order
class ClassicalFinder:
method_name = "classical"
def find_order(self, x, n):
return find_order(x, n)
result = factor(15, ClassicalFinder())
print(result.factors) # [3, 5]
Deutsch-Jozsa Algorithm
Determine whether a Boolean function f: {0,1}^n -> {0,1} is constant or balanced with a single quantum query (classically requires up to 2^(n-1)+1 evaluations).
from shorkin.deutsch_jozsa import deutsch_jozsa
# Constant function (always returns 1)
result = deutsch_jozsa(lambda x: 1, n=3)
print(result.function_type) # "constant"
print(result.is_constant) # True
# Balanced function (returns 1 for odd inputs)
result = deutsch_jozsa(lambda x: x % 2, n=3)
print(result.function_type) # "balanced"
print(result.is_balanced) # True
Grover's Algorithm
Search an unstructured space of 2^n items in O(sqrt(N)) oracle queries (vs O(N) classically).
from shorkin.grover import grover
# Find item 5 in a space of 8
result = grover(lambda x: int(x == 5), n=3)
print(result.found) # 5
print(result.search_space_size) # 8
# Multiple marked items
marked = {3, 5}
result = grover(lambda x: int(x in marked), n=3, num_marked=2)
print(result.found) # 3 or 5
# Reproducible results with a seed
result = grover(lambda x: int(x == 5), n=3, seed=42)
Quantum Teleportation
Transfer an arbitrary single-qubit state using a shared Bell pair and two classical bits.
import math
from shorkin.teleportation import teleport
# Teleport |1> state (theta=pi)
result = teleport(theta=math.pi)
print(result.fidelity) # ~1.0
print(result.success) # True
print(result.alice_bits) # (0, 0), (0, 1), (1, 0), or (1, 1)
# Teleport |+> state (theta=pi/2)
result = teleport(theta=math.pi / 2)
print(result.fidelity) # ~1.0
Quantum Gates
Convenience wrappers around Cirq gates with a fluent GateChain builder.
import cirq
from shorkin.gates import H, CNOT, rotate_x, GateChain
q0, q1 = cirq.LineQubit.range(2)
# Gate aliases
circuit = cirq.Circuit()
circuit.append(H(q0))
circuit.append(CNOT(q0, q1))
# Fluent chaining
import math
chain = GateChain().h(q0).cnot(q0, q1).rz(q1, math.pi / 4)
circuit.append(chain.operations)
QKD Protocols
Three quantum key distribution protocols, all following a common QKDProtocol interface:
| Protocol | Description | Sifting Yield | QBER Threshold |
|---|---|---|---|
| BB84 | 2 bases, 4 states (Bennett-Brassard 1984) | ~50% | ~11% |
| B92 | 2 non-orthogonal states (Bennett 1992) | ~25% | ~5% |
| E91 | Entanglement-based with CHSH Bell test (Ekert 1991) | ~22% | ~11% |
Basic Usage
from shorkin.qkd import BB84, B92, E91, SimulatedChannel
# Create a simulated quantum channel
channel = SimulatedChannel(error_rate=0.02, seed=42)
# Generate a shared key using BB84
protocol = BB84(seed=42)
result = protocol.generate_key(num_qubits=4096, channel=channel)
print(f"Protocol: {result.protocol}")
print(f"QBER: {result.qber:.4f}")
print(f"Key: {result.final_key.hex()}")
print(f"Key bits: {result.key_length_bits}")
Choosing a Protocol
# BB84 -- the standard, good balance of efficiency and security
protocol = BB84(seed=42)
result = protocol.generate_key(num_qubits=4096, channel=channel)
# B92 -- simpler (2 states), lower yield, needs more qubits
protocol = B92(seed=42)
result = protocol.generate_key(num_qubits=8000, channel=channel)
# E91 -- entanglement-based, includes Bell inequality (CHSH) test
protocol = E91(seed=42)
result = protocol.generate_key(num_qubits=20000, channel=channel)
print(f"CHSH value: {result.metadata['chsh_value']:.3f}") # > 2.0 = secure
Encrypt / Decrypt with QKD Keys
from shorkin.qkd import BB84, SimulatedChannel, encrypt, decrypt
channel = SimulatedChannel(seed=42)
result = BB84(seed=42).generate_key(num_qubits=4096, channel=channel)
ciphertext = encrypt(b"secret payload", result.final_key)
plaintext = decrypt(ciphertext, result.final_key)
assert plaintext == b"secret payload"
Simulated Channel Options
from shorkin.qkd import SimulatedChannel
# Perfect channel (no errors, no loss)
channel = SimulatedChannel()
# Noisy channel (5% bit-flip error rate)
channel = SimulatedChannel(error_rate=0.05)
# Lossy channel (10% photon loss)
channel = SimulatedChannel(loss_rate=0.10)
# Both noise and loss
channel = SimulatedChannel(error_rate=0.03, loss_rate=0.05, seed=42)
The QuantumChannel is a typing.Protocol -- you can implement your own to connect to real quantum hardware.
HTTP Integration
QKD key exchange runs at /.well-known/qkd/initiate. Once a session is established, request and response bodies are AES-256-GCM encrypted using the QKD-derived key, identified by the X-QKD-Session-Id header.
FastAPI
from fastapi import Depends, FastAPI
from shorkin.transport.http.fastapi import QKDMiddleware, QKDSession, qkd_session
app = FastAPI()
# Option 1: Middleware (encrypts all responses for active sessions)
app.add_middleware(QKDMiddleware, protocol="bb84")
# Option 2: Dependency injection (per-route control)
@app.get("/secure")
async def secure_endpoint(qkd: QKDSession = Depends(qkd_session("bb84"))):
if qkd.is_active:
return {"encrypted": qkd.encrypt(b"secret").hex()}
return {"message": "no QKD session"}
Client usage with FastAPI
import httpx
# Step 1: Initiate key exchange
resp = httpx.post("http://localhost:8000/.well-known/qkd/initiate", json={
"protocol": "bb84",
"peer_id": "my-client",
})
session_id = resp.json()["session_id"]
# Step 2: Make requests with the session
resp = httpx.get("http://localhost:8000/secure", headers={
"X-QKD-Session-Id": session_id,
})
# Response body is AES-256-GCM encrypted
Starlette
from starlette.applications import Starlette
from shorkin.transport.http.starlette import QKDMiddleware
app = Starlette(routes=[...])
app.add_middleware(QKDMiddleware, protocol="bb84")
Django
# settings.py
MIDDLEWARE = [
# ...
"shorkin.transport.http.django.QKDMiddleware",
]
SHORKIN_QKD = {
"PROTOCOL": "bb84", # or "b92", "e91"
"NUM_QUBITS": 4096,
"TARGET_KEY_BITS": 256,
"STRICT": False, # True = reject requests without QKD session
}
Tornado
import tornado.web
from shorkin.transport.http.tornado import QKDRequestMixin, qkd_routes
class SecureHandler(QKDRequestMixin, tornado.web.RequestHandler):
def get(self):
self.write(b"encrypted if QKD session is active")
app = tornado.web.Application(
qkd_routes("bb84") + [
(r"/secure", SecureHandler, {"qkd_protocol": "bb84"}),
]
)
Strict Mode
All HTTP integrations support strict=True, which rejects any request without an active QKD session (returns 403). QKD negotiation endpoints are always accessible.
gRPC Integration
QKD key exchange is exposed as a QKDKeyExchange gRPC service. Client and server interceptors handle session metadata transparently.
Server
from concurrent import futures
import grpc
from shorkin.transport.grpc.servicer import QKDKeyExchangeServicer
from shorkin.transport.grpc.interceptor import QKDServerInterceptor
from shorkin.transport.grpc import qkd_pb2_grpc
interceptor = QKDServerInterceptor(protocol="bb84")
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=10),
interceptors=[interceptor],
)
servicer = QKDKeyExchangeServicer(protocol="bb84")
qkd_pb2_grpc.add_QKDKeyExchangeServicer_to_server(servicer, server)
server.add_insecure_port("[::]:50051")
server.start()
server.wait_for_termination()
Client
import grpc
from shorkin.transport.grpc.interceptor import QKDClientInterceptor
from shorkin.transport.grpc import qkd_pb2, qkd_pb2_grpc
# Set up intercepted channel
interceptor = QKDClientInterceptor(protocol="bb84")
channel = grpc.intercept_channel(
grpc.insecure_channel("localhost:50051"),
interceptor,
)
stub = qkd_pb2_grpc.QKDKeyExchangeStub(channel)
# Initiate key exchange
response = stub.Initiate(qkd_pb2.KeyExchangeInitRequest(
protocol="bb84",
peer_id="my-service",
))
print(f"Session: {response.session_id}")
print(f"Status: {response.status}")
# Set session for subsequent calls
interceptor.session_id = response.session_id
Architecture
QKD and Classical Networks
True QKD requires a physical quantum channel (photon transmission over fiber optics). This library simulates the quantum channel using Cirq, making it suitable for:
- Education and prototyping -- learn and develop QKD-secured applications without quantum hardware
- Integration testing -- validate your application's encryption pipeline end-to-end
- Research -- experiment with protocol parameters, noise models, and error thresholds
The classical post-processing (basis reconciliation, error estimation, privacy amplification) runs over standard HTTP/gRPC and is protocol-accurate. The QuantumChannel abstraction is pluggable -- swap SimulatedChannel for a real hardware adapter without changing application code.
Package Structure
shorkin/
classical/ # Classical order finding (brute-force)
quantum/ # Quantum order finding (Cirq QPE)
factor.py # Shor's algorithm orchestration
deutsch_jozsa.py # Deutsch-Jozsa algorithm
grover.py # Grover's search algorithm
teleportation.py # Quantum teleportation protocol
gates.py # Gate aliases, helpers, and GateChain builder
cli.py # CLI entry point
qkd/ # QKD protocol engines
bb84.py # BB84 protocol
b92.py # B92 protocol
e91.py # E91 protocol
channel.py # QuantumChannel Protocol + SimulatedChannel
encryption.py # AES-256-GCM encrypt/decrypt
key_store.py # Session key storage with rotation/expiration
transport/ # HTTP & gRPC integrations
http/
fastapi.py # FastAPI middleware + Depends()
starlette.py
django.py
tornado.py
grpc/
servicer.py # QKDKeyExchange gRPC service
interceptor.py # Client + server interceptors
Key Types
from shorkin.qkd import QKDResult
result: QKDResult
result.final_key # bytes -- the usable symmetric key
result.raw_key # bytes -- key before privacy amplification
result.protocol # str -- "bb84", "b92", or "e91"
result.qber # float -- quantum bit error rate
result.key_length_bits # int -- final key length in bits
result.initial_qubit_count # int -- qubits transmitted
result.sifted_key_length # int -- bits after sifting
result.amplified # bool -- privacy amplification applied
result.metadata # dict -- protocol-specific (e.g., chsh_value for E91)
Development
# Clone and install in dev mode
git clone https://github.com/yourusername/shorkin.git
cd shorkin
pip install -e ".[all]"
# Run tests
pytest
# Run only QKD tests
pytest tests/qkd/
# Run only transport tests
pytest tests/transport/
# Run only the original Shor tests
pytest tests/test_*.py
License
MIT
Release files for shorkin 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| shorkin-1.1.1.tar.gz | 52.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shorkin-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:109.5 kB
Release files / shorkin-1.1.1.tar.gz
| Download URL | shorkin-1.1.1.tar.gz |
|---|---|
| Size | 52.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b557ab82f734702cf2ba95c495606908227a968c6efda5c6e0e984223a6182f4
|
|
BLAKE2b-256 checksum How to use checksums |
85961b4fd4907673e7e0bd41430f95067c26da89be8d53d2f9899ff2918ce5c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 1, 2026.
Transparency logRelease files / shorkin-1.1.1-py3-none-any.whl
| Download URL | shorkin-1.1.1-py3-none-any.whl |
|---|---|
| Size | 57.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0a9ccd797c941d418abda1f704cf8f8e2dc517b46db1f6ee4e9b200ecc18fb67
|
|
BLAKE2b-256 checksum How to use checksums |
e7920fd8dbe5733f4e75ed9df6667e851d0a2499c614c2a41d87c538d628b173
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 1, 2026.
Transparency log