Research preview

Key Management

PEM files, rotation, storage, permissions, and operational key hygiene.

Skip to content

Key types ​

MaterialSizeSensitivityLifetime
Public key800 BPublic — can be distributed freelyMatches key pair lifetime
Private key1 248 BSecret — protect at all costsMonths to years (rotate on policy)
Ciphertext768 BPer-session — safe to transmitSingle use
Shared secret32 BSecret — derive session keys, then discardSingle session

Generating keys ​

python
from vortex_pqc import generate_keypair

kp = generate_keypair()
# kp.public_key  → 800 bytes
# kp.private_key → 1248 bytes

Each 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 0600

Reading 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 ​

KindHeader
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 ​

EnvironmentPublic keyPrivate key
DevelopmentPEM file in project (gitignored)PEM file, mode 0600, gitignored
Server / VMConfig directory, world-readable OKEncrypted volume or secrets manager
ContainerConfigMap / env varKubernetes Secret / Vault / sealed secret
Embedded / IoTFlash, provisioned at factorySecure 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 ​

TriggerAction
Scheduled (e.g. annually)Generate new pair, update distribution
Personnel changeRotate immediately
Suspected compromiseRotate immediately, investigate
Algorithm migrationGenerate 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