Key types
| Material | Size | Sensitivity | Lifetime |
|---|---|---|---|
| Public key | 800 B | Public — can be distributed freely | Matches key pair lifetime |
| Private key | 1 248 B | Secret — protect at all costs | Months to years (rotate on policy) |
| Ciphertext | 768 B | Per-session — safe to transmit | Single use |
| Shared secret | 32 B | Secret — derive session keys, then discard | Single session |
Generating keys
python
from vortex_pqc import generate_keypair
kp = generate_keypair()
# kp.public_key → 800 bytes
# kp.private_key → 1248 bytesEach call produces a fresh, statistically independent key pair using the OS CSPRNG.
PEM file storage
Writing keys
python
from vortex_pqc import PEMKind, write_pem_file, generate_keypair
kp = generate_keypair()
write_pem_file("server.pub", PEMKind.PUBLIC_KEY, kp.public_key)
write_pem_file("server.key", PEMKind.PRIVATE_KEY, kp.private_key)
# server.key is automatically chmod 0600Reading keys
python
from vortex_pqc import PEMKind, read_pem_file
pk = read_pem_file("server.pub", PEMKind.PUBLIC_KEY)
sk = read_pem_file("server.key", PEMKind.PRIVATE_KEY)PEM labels
| Kind | Header |
|---|---|
| Public key | -----BEGIN VORTEX256 PUBLIC KEY----- |
| Private key | -----BEGIN VORTEX256 PRIVATE KEY----- |
| Ciphertext | -----BEGIN VORTEX256 CIPHERTEXT----- |
| Shared secret | -----BEGIN VORTEX256 SHARED SECRET----- |
→ Full spec: PEM format
Storage recommendations
| Environment | Public key | Private key |
|---|---|---|
| Development | PEM file in project (gitignored) | PEM file, mode 0600, gitignored |
| Server / VM | Config directory, world-readable OK | Encrypted volume or secrets manager |
| Container | ConfigMap / env var | Kubernetes Secret / Vault / sealed secret |
| Embedded / IoT | Flash, provisioned at factory | Secure element, TPM, or encrypted flash |
Key rotation
python
from vortex_pqc import generate_keypair, PEMKind, write_pem_file
def rotate_keys(path_prefix: str) -> None:
"""Generate new key pair and archive old ones."""
import shutil
from pathlib import Path
for suffix in ("pub", "key"):
old = Path(f"{path_prefix}.{suffix}")
if old.exists():
shutil.move(old, old.with_suffix(".bak"))
kp = generate_keypair()
write_pem_file(f"{path_prefix}.pub", PEMKind.PUBLIC_KEY, kp.public_key)
write_pem_file(f"{path_prefix}.key", PEMKind.PRIVATE_KEY, kp.private_key)Rotation policy guidelines
| Trigger | Action |
|---|---|
| Scheduled (e.g. annually) | Generate new pair, update distribution |
| Personnel change | Rotate immediately |
| Suspected compromise | Rotate immediately, investigate |
| Algorithm migration | Generate new pair with new library version |
Private key anatomy
Understanding what's inside the 1248-byte private key:
┌────────────┬──────────────────────────────────────────┐
│ Bytes │ Content │
├────────────┼──────────────────────────────────────────┤
│ 0 – 383 │ pack(s) — secret polynomial │
│ 384 – 1183 │ Public key (embedded copy) │
│ 1184–1215 │ H(pk) — SHA3-256 hash of public key │
│ 1216–1247 │ z — implicit rejection token │
└────────────┴──────────────────────────────────────────┘Never expose bytes 0–383 or 1216–1247. The embedded public key (384–1183) is safe to share.
.gitignore template
gitignore
# VORTEX key material — never commit
*.key
*.pem
server.key
server.pub