Skip to main content

Migration Guide — Transition to Post-Quantum Cryptography

This guide provides a practical, step-by-step plan for migrating your applications from traditional cryptography (RSA, ECDSA, AES with classical key exchange) to Qpher post-quantum cryptography (Kyber768 for encryption, Dilithium3 for signatures).

Prerequisites

  • A Qpher account (Free tier is sufficient to start)
  • An understanding of which cryptographic operations your application currently performs
  • Access to modify your application code and data storage schemas

Why Migrate Now?

The "Harvest Now, Decrypt Later" Threat

Your Encrypted Data Is Already at Risk

Nation-state adversaries are actively intercepting and storing encrypted communications today, planning to decrypt them once quantum computers become available. This is known as the "Harvest Now, Decrypt Later" (HNDL) attack. Data encrypted with RSA or ECDH today could be decrypted in 5-10 years. If your data has a sensitivity lifetime longer than that, you need to migrate to PQC now.

Timeline Context

YearMilestone
2024NIST finalized ML-KEM (Kyber) and ML-DSA (Dilithium) standards
2025Early adopters deploying PQC in production
2026Government mandates beginning (CNSA 2.0 timeline)
2030NIST deprecation deadline for many classical algorithms
2030-2035Estimated arrival of cryptographically relevant quantum computers

Organizations with long-lived data (healthcare, finance, government, legal) should begin migration immediately.

Migration Assessment

Step 1: Inventory Your Cryptographic Usage

Catalog every place your application uses cryptography:

CategoryTraditionalQpher PQC Replacement
Data encryption (at rest)RSA-OAEP, AES with RSA key wrapKyber768 KEM (hybrid KEM-DEM)
Data encryption (in transit)TLS with ECDH key exchangeKyber768 KEM for key agreement
Digital signaturesRSA-PSS, ECDSADilithium3
Document signingRSA, ECDSADilithium3
API authenticationHMAC-SHA256HMAC-SHA256 (no change needed)
Password hashingbcrypt, Argon2bcrypt, Argon2 (no change needed)
Not Everything Needs to Change

Symmetric algorithms (AES-256-GCM) and hash-based constructions (HMAC-SHA256, bcrypt) are already quantum-resistant at current key sizes. You only need to replace asymmetric cryptography: RSA, ECDSA, ECDH, and classical key exchange mechanisms.

Step 2: Prioritize by Data Sensitivity

Prioritize migration based on how long your data needs to remain confidential:

PriorityData TypeExampleWhy
P0 — ImmediateLong-lived secretsHealthcare records, financial data, legal contractsVulnerable to HNDL attack today
P1 — HighBusiness-critical signaturesSigned contracts, audit trails, compliance recordsIntegrity must hold for decades
P2 — MediumSession data, short-lived tokensAPI tokens, session keysLow risk — short sensitivity window
P3 — LowPublic data with integrityPublic announcements, open-source signaturesCan migrate on a relaxed timeline

Migration Strategies

Replace traditional cryptography directly with Qpher PQC:

Before and After — File Encryption
# BEFORE: RSA encryption (vulnerable to quantum)
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

private_key = rsa.generate_private_key(65537, 2048)
public_key = private_key.public_key()

ciphertext = public_key.encrypt(
    plaintext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None,
    ),
)

During the transition period, encrypt data with both traditional and PQC algorithms. This provides backward compatibility while building quantum resistance:

Hybrid Encryption Approach
import base64
import requests
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

def hybrid_encrypt(plaintext: bytes, rsa_public_key, qpher_key_version: int):
    """Encrypt with both RSA and Qpher PQC during transition."""
    # Traditional RSA encryption (for backward compatibility)
    rsa_ciphertext = rsa_public_key.encrypt(
        plaintext,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None,
        ),
    )

    # Qpher PQC encryption (quantum-resistant)
    encoded = base64.b64encode(plaintext).decode()
    pqc_response = requests.post(
        "https://api.qpher.ai/api/v1/kem/encrypt",
        headers={
            "Content-Type": "application/json",
            "x-api-key": "qph_your_key_here",
        },
        json={
            "plaintext": encoded,
            "key_version": qpher_key_version,
        },
    )
    pqc_data = pqc_response.json()["data"]

    # Store both ciphertexts
    return {
        "rsa_ciphertext": base64.b64encode(rsa_ciphertext).decode(),
        "pqc_ciphertext": pqc_data["ciphertext"],
        "pqc_key_version": pqc_data["key_version"],
        "encryption_mode": "hybrid",
    }

Strategy C: Re-Encryption of Historical Data

For existing encrypted data, decrypt with your current system and re-encrypt with Qpher:

Re-Encrypt Historical Data
import base64
import requests

def migrate_record(record, rsa_private_key, qpher_key_version: int):
    """Migrate a single record from RSA to Qpher PQC."""
    # Step 1: Decrypt with existing RSA key
    plaintext = rsa_private_key.decrypt(
        base64.b64decode(record["rsa_ciphertext"]),
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None,
        ),
    )

    # Step 2: Re-encrypt with Qpher PQC
    encoded = base64.b64encode(plaintext).decode()
    response = requests.post(
        "https://api.qpher.ai/api/v1/kem/encrypt",
        headers={
            "Content-Type": "application/json",
            "x-api-key": "qph_your_key_here",
        },
        json={
            "plaintext": encoded,
            "key_version": qpher_key_version,
        },
    )
    pqc_data = response.json()["data"]

    # Step 3: Update the record
    record["pqc_ciphertext"] = pqc_data["ciphertext"]
    record["pqc_key_version"] = pqc_data["key_version"]
    record["migration_status"] = "completed"

    return record

# Batch migration
for record in database.query_unmigrated_records(batch_size=100):
    migrated = migrate_record(record, rsa_private_key, qpher_key_version=1)
    database.update(migrated)
    print(f"Migrated record {record['id']}")

Migration for Document Signing

Replace RSA/ECDSA Signatures with Dilithium3

Before and After — Document Signing
# BEFORE: ECDSA signing (vulnerable to quantum)
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes

private_key = ec.generate_private_key(ec.SECP256R1())
signature = private_key.sign(document_hash, ec.ECDSA(hashes.SHA256()))
Signature Size Difference

Dilithium3 signatures are 3,293 bytes compared to 64 bytes for ECDSA P-256 or 256 bytes for RSA-2048. Plan your storage and bandwidth accordingly. For most applications, this increase is negligible relative to document sizes.

Database Schema Updates

Add columns to store PQC-encrypted data alongside or replacing traditional fields:

-- Add PQC columns to existing tables
ALTER TABLE sensitive_records ADD COLUMN pqc_ciphertext TEXT;
ALTER TABLE sensitive_records ADD COLUMN pqc_key_version INTEGER;
ALTER TABLE sensitive_records ADD COLUMN encryption_type VARCHAR(20)
DEFAULT 'rsa' CHECK (encryption_type IN ('rsa', 'pqc', 'hybrid'));

-- Add PQC signature columns
ALTER TABLE signed_documents ADD COLUMN pqc_signature TEXT;
ALTER TABLE signed_documents ADD COLUMN pqc_sig_key_version INTEGER;
ALTER TABLE signed_documents ADD COLUMN signature_type VARCHAR(20)
DEFAULT 'ecdsa' CHECK (signature_type IN ('ecdsa', 'dilithium3', 'hybrid'));
PhaseDurationActivities
Assessment1-2 weeksInventory cryptographic usage, prioritize data, create Qpher account
Pilot2-4 weeksMigrate one non-critical service, validate performance and correctness
Hybrid rollout4-8 weeksDeploy hybrid encryption for P0 data, dual-write both traditional and PQC
Full migration8-16 weeksRe-encrypt historical data, migrate remaining services
Cleanup2-4 weeksRemove traditional encryption code paths, archive old keys
Start with the Free Tier

Qpher Free tier includes 100 operations per month — enough to prototype and validate your migration plan before committing to a paid plan. Use it for your pilot phase.

Verification Checklist

After migration, verify each of these:

  • All new data is encrypted with Qpher Kyber768
  • All new signatures use Qpher Dilithium3
  • Historical data has been re-encrypted (or hybrid-encrypted)
  • key_version is stored alongside every ciphertext and signature
  • Decryption of migrated data returns correct plaintext
  • Signature verification succeeds for migrated documents
  • Error handling covers Qpher API failures gracefully
  • Performance meets your latency requirements (p95: <15ms encrypt, <30ms sign)
  • Monitoring and alerting are in place for PQC operations

Error Handling

HTTP StatusError CodeCauseResolution
400ERR_INVALID_001Invalid request formatCheck request body matches the API schema
401ERR_AUTH_001Invalid API keyVerify your API key is active
429ERR_RATE_001Rate limit exceeded during batch migrationAdd delays between batches or upgrade your plan
500ERR_INTERNAL_001Server error during migrationRetry with exponential backoff