Metadata-Version: 2.4
Name: shorkin
Version: 1.1.1
Summary: Quantum computing toolkit: Shor's, Grover's, Deutsch-Jozsa, teleportation, gates, and QKD protocols
License-Expression: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: cirq-core<2.0,>=1.3
Requires-Dist: sympy>=1.12
Requires-Dist: cryptography>=41.0
Requires-Dist: numpy>=1.24
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-timeout>=2.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: httpx>=0.24; extra == "dev"
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100; extra == "fastapi"
Requires-Dist: starlette>=0.27; extra == "fastapi"
Provides-Extra: starlette
Requires-Dist: starlette>=0.27; extra == "starlette"
Provides-Extra: django
Requires-Dist: django>=4.2; extra == "django"
Provides-Extra: tornado
Requires-Dist: tornado>=6.3; extra == "tornado"
Provides-Extra: grpc
Requires-Dist: grpcio>=1.57; extra == "grpc"
Requires-Dist: grpcio-tools>=1.57; extra == "grpc"
Requires-Dist: protobuf>=4.24; extra == "grpc"
Provides-Extra: http
Requires-Dist: shorkin[django,fastapi,starlette,tornado]; extra == "http"
Provides-Extra: all
Requires-Dist: shorkin[dev,grpc,http]; extra == "all"

# 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](https://quantumai.google/cirq). QKD-derived keys are used for AES-256-GCM payload encryption.

## Installation

```bash
# 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

```bash
# 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

```python
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).

```python
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).

```python
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.

```python
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.

```python
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

```python
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

```python
# 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

```python
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

```python
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

```python
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

```python
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

```python
from starlette.applications import Starlette
from shorkin.transport.http.starlette import QKDMiddleware

app = Starlette(routes=[...])
app.add_middleware(QKDMiddleware, protocol="bb84")
```

### Django

```python
# 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

```python
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

```python
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

```python
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

```python
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

```bash
# 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
