Migrate from liboqs to Qpher
This guide walks you through replacing direct liboqs usage with the Qpher managed PQC API. By the end, you will have eliminated C compilation dependencies and gained managed key lifecycle, audit logging, and multi-tenant isolation.
Why Migrate?
- No more compiling C dependencies — liboqs requires building C code and managing native library paths; Qpher is a REST API with thin SDK wrappers
- Managed key lifecycle — generate, rotate, retire, and version keys via API instead of implementing your own key storage
- Multi-tenant isolation — built-in tenant scoping for B2B SaaS use cases
- Hybrid PQC+Classical — X-Wing KEM and Composite ML-DSA available without manual implementation
- Audit logging — every cryptographic operation is logged with tenant, key version, and timestamp
- SOC 2 compliance roadmap — Qpher is on a path to SOC 2 certification
API Mapping
| liboqs Operation | Qpher Equivalent |
|---|---|
oqs.KeyEncapsulation('Kyber768').generate_keypair() | POST /api/v1/kms/keys/generate {"algorithm": "Kyber768"} |
kem.encap_secret(public_key) | POST /api/v1/kem/encapsulate {"key_version": 1} |
kem.decap_secret(ciphertext) | POST /api/v1/kem/decapsulate {"kem_ciphertext": "...", "key_version": 1} |
oqs.Signature('Dilithium3').sign(msg) | POST /api/v1/signature/sign {"message": "base64...", "key_version": 1} |
sig.verify(msg, signature, public_key) | POST /api/v1/signature/verify {"message": "base64...", "signature": "base64...", "key_version": 1} |
Before: liboqs (Python)
import oqs
# Key generation — you must securely store the private key yourself
kem = oqs.KeyEncapsulation('Kyber768')
public_key = kem.generate_keypair()
# private_key = kem.export_secret_key() # You manage storage, encryption, rotation
# Encrypt — returns raw shared secret, you build KEM-DEM yourself
ciphertext, shared_secret = kem.encap_secret(public_key)
# Decrypt — you must load the private key from your storage
recovered_secret = kem.decap_secret(ciphertext)
# Signing — same pattern: you manage key pairs
sig = oqs.Signature('Dilithium3')
public_key_sig = sig.generate_keypair()
signature = sig.sign(b"Hello, world!")
is_valid = sig.verify(b"Hello, world!", signature, public_key_sig)
Problems with this approach:
- You must compile liboqs from C source and manage native library paths
- You are responsible for private key storage, encryption at rest, and access control
- Key rotation, versioning, and retirement are entirely DIY
- No audit trail unless you build one
- Multi-tenant isolation requires custom implementation
After: Qpher (Python SDK)
from qpher import Qpher
client = Qpher(api_key="qph_live_your_api_key")
# Key generation — Qpher stores and encrypts the private key
key = client.keys.generate(algorithm="Kyber768")
print(f"Key version: {key.key_version}")
# Encrypt — KEM-DEM handled automatically, returns ciphertext
result = client.kem.encrypt(
plaintext=b"sensitive data",
key_version=key.key_version,
)
# Decrypt — private key never leaves Qpher's enclave
decrypted = client.kem.decrypt(
ciphertext=result.ciphertext,
key_version=key.key_version,
)
# Signing
sig_key = client.keys.generate(algorithm="Dilithium3")
signature = client.signatures.sign(
message=b"Hello, world!",
key_version=sig_key.key_version,
)
is_valid = client.signatures.verify(
message=b"Hello, world!",
signature=signature.signature,
key_version=sig_key.key_version,
)
After: Qpher (Node.js SDK)
import { Qpher } from "@qpher/sdk";
const client = new Qpher({ apiKey: "qph_live_your_api_key" });
// Key generation
const key = await client.keys.generate({ algorithm: "Kyber768" });
// Encrypt
const result = await client.kem.encrypt({
plaintext: Buffer.from("sensitive data"),
keyVersion: key.keyVersion,
});
// Decrypt
const decrypted = await client.kem.decrypt({
ciphertext: result.ciphertext,
keyVersion: key.keyVersion,
});
After: Qpher (cURL)
# Generate key pair
curl -X POST https://api.qpher.ai/api/v1/kms/keys/generate \
-H "x-api-key: qph_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"algorithm": "Kyber768"}'
# Encrypt
curl -X POST https://api.qpher.ai/api/v1/kem/encrypt \
-H "x-api-key: qph_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"plaintext": "c2Vuc2l0aXZlIGRhdGE=", "key_version": 1}'
# Decrypt
curl -X POST https://api.qpher.ai/api/v1/kem/decrypt \
-H "x-api-key: qph_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"ciphertext": "...", "key_version": 1}'
Step-by-Step Migration
Step 1: Sign Up
Create an account at portal.qpher.ai and get your API key.
Step 2: Install the SDK
# Python
pip install qpher
# Node.js
npm install @qpher/sdk
# Go
go get github.com/qpher/qpher-go
Step 3: Generate PQC Keys
Replace your liboqs key generation with Qpher API calls. Each call creates a key pair where the private key is stored and encrypted by Qpher.
from qpher import Qpher
client = Qpher(api_key="qph_live_your_api_key")
# Generate KEM key pair (replaces oqs.KeyEncapsulation('Kyber768').generate_keypair())
kem_key = client.keys.generate(algorithm="Kyber768")
# Generate signature key pair (replaces oqs.Signature('Dilithium3').generate_keypair())
sig_key = client.keys.generate(algorithm="Dilithium3")
Step 4: Replace Crypto Operations
Swap liboqs calls with Qpher SDK calls. See the API mapping table above.
Step 5: Remove liboqs from CI
# requirements.txt
- liboqs-python>=0.14.0
+ qpher>=0.1.0
# Dockerfile — remove C compilation
- RUN apt-get install -y cmake gcc libssl-dev
- RUN pip install liboqs-python
+ RUN pip install qpher
Step 6: Verify
Run your test suite against the Qpher API. The Free tier includes 100 operations per month for testing.
What You Keep
- Same algorithms: Qpher uses ML-KEM-768 (Kyber768) and ML-DSA-65 (Dilithium3) — the same NIST-standardized algorithms available in liboqs
- Same security level: NIST Level 3 for both KEM and signatures
- Base64 encoding: Qpher APIs accept and return base64-encoded binary data, same as most liboqs Python examples
What Changes
| Before (liboqs) | After (Qpher) |
|---|---|
| Local function call | HTTP API call (~150ms) |
| You store private keys | Qpher stores private keys |
| You implement KEM-DEM | KEM-DEM built into /kem/encrypt |
| No key versioning | Explicit key_version on every call |
| No audit trail | Automatic audit logging |
| C compilation required | Pure Python/JS/Go SDK |