What if an application could change its cryptographic strategy without contacting a server, downloading a security policy, or depending on a cloud service?
That is the idea behind CryptoFlex.
CryptoFlex is a local-first crypto-agility policy engine for Python that makes cryptographic decisions entirely inside the application environment.
No network calls.
No telemetry.
No remote policy server.
No third-party decision-making service.
Just local cryptographic capabilities, local policy, and an application-defined security requirement.
🔐 Why local-first?
Most modern security systems are increasingly connected to external services.
But there are applications where that isn't desirable or even possible.
Consider:
- Offline file-encryption tools
- Desktop security applications
- Embedded devices
- Air-gapped environments
- Local-first software
- Systems with strict data-isolation requirements
In these environments, asking a remote service:
"Which cryptographic algorithm should I use?"
doesn't make much sense.
CryptoFlex is designed around a different idea:
Application
│
▼
┌─────────────────┐
│ CryptoFlex │
│ Policy Engine │
└────────┬────────┘
│
LOCAL DECISION ONLY
│
┌───────────┼───────────┐
▼ ▼ ▼
X25519 X25519 + X25519 +
ML-KEM-768 ML-KEM-1024
The decision is made locally.
🧠 What makes CryptoFlex different?
CryptoFlex isn't trying to create a new cryptographic algorithm.
Instead, it sits above existing cryptographic primitives.
The project currently supports:
| Profile | Cryptographic sources | Quantum-safe |
|---|---|---|
classical_only |
X25519 | No |
hybrid_standard |
X25519 + ML-KEM-768 | Yes |
hybrid_high |
X25519 + ML-KEM-1024 | Yes |
The application doesn't have to hard-code one of these combinations throughout its architecture.
Instead, the PolicyEngine can select a profile based on local information such as:
- Available cryptographic implementations
- Security/performance constraints
- A versioned local risk table
This is where the crypto-agility idea comes in.
🔄 From algorithm lock-in to crypto-agility
A tightly coupled application might look like:
key_exchange = X25519()
The application now assumes X25519 everywhere.
If its security requirements change later, replacing the algorithm can become an architectural problem.
With a policy layer, the application can instead express something closer to:
constraint = Constraint.BALANCED
CryptoFlex determines which supported profile satisfies that requirement.
Today that could mean:
X25519
Tomorrow:
X25519 + ML-KEM-768
And later:
X25519 + ML-KEM-1024
The goal is to make the cryptographic policy replaceable without tightly coupling it to the rest of the application.
⚔️ Why hybrid cryptography?
CryptoFlex supports hybrid classical + post-quantum configurations.
For example:
X25519
+
ML-KEM-768
│
▼
Combined key material
│
▼
HKDF
│
▼
Root key
The classical and post-quantum mechanisms contribute to the resulting key establishment.
But there is an important distinction:
CryptoFlex did not invent hybrid cryptography.
X25519, ML-KEM, and hybrid key-exchange designs already exist.
The focus of CryptoFlex is the local policy and orchestration layer around these established primitives.
🗂️ Local-first + migration-aware
Crypto-agility creates another interesting problem.
What happens when the default cryptographic policy changes?
Imagine:
2026
hybrid_standard
X25519 + ML-KEM-768
Later:
Future
hybrid_high
X25519 + ML-KEM-1024
What happens to files encrypted under the previous policy?
CryptoFlex addresses this with a versioned header format.
The header can preserve information about the cryptographic profile associated with the encrypted data.
So changing the current policy doesn't automatically mean rewriting every previously encrypted file.
This is an important part of making cryptographic migration practical.
🛡️ No silent security downgrade
Another design choice is how CryptoFlex handles unavailable PQC support.
If ML-KEM isn't available, the engine can fall back to:
classical_only
But the decision exposes:
decision.degraded
decision.reason
So the application can detect that the selected configuration is weaker than the preferred configuration.
For applications that don't want this fallback, CryptoFlex supports:
require_quantum_safe=True
For example:
decision = engine.decide(
Constraint.BALANCED,
require_quantum_safe=True
)
Instead of silently continuing with a classical configuration, the application can fail when its quantum-safe requirement cannot be satisfied.
🌐 No network dependency
This is probably the feature I care about most.
CryptoFlex does not depend on:
- Cloud security services
- Remote algorithm-selection APIs
- Live threat feeds
- Telemetry systems
- Network connectivity
The risk table is bundled locally and can change through normal package releases.
That means the policy decision remains inside the application's security boundary.
🔑 What happens underneath?
CryptoFlex uses established libraries rather than implementing cryptographic mathematics itself.
The project uses:
-
cryptographyfor X25519 and HKDF -
liboqs-pythonfor ML-KEM
The combined key material is processed through HKDF.
Conceptually:
Classical shared secret
+
PQC shared secret
│
▼
HKDF
│
▼
Root key
The project follows established hybrid key-combination design principles rather than introducing a new cryptographic construction.
🐍 Using CryptoFlex
A basic example:
from cryptoflex import (
PolicyEngine,
Constraint,
establish_keys,
derive_root_key,
recover_root_key
)
engine = PolicyEngine()
keyset = establish_keys(
engine,
constraint=Constraint.BALANCED
)
print(keyset.policy_decision.reason)
derived = derive_root_key(
keyset.public_bundle
)
root_key = recover_root_key(
keyset.private_handles,
derived.header
)
assert root_key == derived.root_key
The consuming application can then use the derived root key with its own encryption layer.
🎯 So what's actually unique?
CryptoFlex isn't claiming that any individual cryptographic primitive is new.
The interesting part is the combination of:
Local-first
*
Runtime cryptographic policy
*
Crypto-agility
*
Classical + post-quantum profiles
*
Migration-aware encrypted-data headers
*
Explicit degraded-state handling
All inside a Python library designed specifically around local/offline applications.
That's the problem I'm exploring.
⚠️ Current status
CryptoFlex is currently v0.1.0 and unaudited.
It is an early project, not a production-ready cryptographic library.
I built it to explore how applications could be designed for cryptographic migration without becoming permanently coupled to one cryptographic stack.
🚀 The project
GitHub:
https://github.com/keerthivasan-sankar/crypto_flex
I'm especially interested in feedback on the PolicyEngine architecture, local risk model, migration design, and API.
If you were building a crypto-agility layer for an offline/local application, what would you change?
Top comments (0)