Skip to main content

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 OperationQpher 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 callHTTP API call (~150ms)
You store private keysQpher stores private keys
You implement KEM-DEMKEM-DEM built into /kem/encrypt
No key versioningExplicit key_version on every call
No audit trailAutomatic audit logging
C compilation requiredPure Python/JS/Go SDK