PS Product SecurityKnowledge Base

๐Ÿ” Crypto Design โ€” Algorithm Choice, Key Hierarchy, Envelope Encryption, Signing, Rotation, and Common Coding Mistakes

Intro: Good crypto design starts long before the first library call. Most failures are not caused by attackers breaking AES; they are caused by teams choosing the wrong primitive, mixing up key roles, reusing nonces, storing keys badly, or skipping rotation and verification.

What this page includes

  • a practical crypto-design decision model
  • key hierarchy and envelope-encryption patterns
  • signing and verification design rules
  • rotation ownership and operating model
  • current algorithm guidance, weak/deprecated choices, and post-quantum direction
  • top weak-crypto mistakes in Python, Java, and PHP code

Start with the system questions, not the library

Before choosing an algorithm, ask:

  1. What are we protecting? password, token, database field, object, backup, message, API call, artifact, or certificate?
  2. Do we need confidentiality, integrity, authenticity, or all three?
  3. Does the data need to be recovered later?
  4. Is the control for data at rest, in transit, or both?
  5. Who is allowed to decrypt, sign, rotate, or revoke?
  6. What breaks if the key is rotated, revoked, or unavailable?

What to encrypt at rest vs in transit

Data state What to protect Typical control
At rest DB fields, backups, blob/object storage, message persistence, config secrets storage encryption, application-layer field encryption, tokenization, KMS-wrapped data keys
In transit browser โ†” edge, service โ†” service, API โ†” DB proxy, queue producers โ†” brokers TLS 1.3, mTLS where needed, signed tokens, message signing in special cases
At use / in memory short-lived secrets, plaintext after decryption, signing keys loaded into process memory minimize plaintext lifetime, isolate workloads, prefer managed key operations/HSM where practical

Good rule of thumb

  • Use TLS for data in transit.
  • Use encryption at rest for broad platform coverage.
  • Add application-layer encryption when platform encryption is not enough because too many systems or operators can still read the plaintext.

Key hierarchy

A strong key hierarchy reduces blast radius and clarifies ownership.

flowchart TD A[Root of trust / HSM / cloud KMS root] --> B[KEK / wrapping key] B --> C[DEK / data key for dataset or object] C --> D[Encrypted data] A --> E[Signing key] A --> F[Separate admin / break-glass controls]
Key type Purpose Typical owner
Root / HSM-protected key trust anchor inside KMS/HSM boundary platform / cloud security / key-management function
KEK / wrapping key encrypts data keys or protects service-specific keys platform or service security owner
DEK / data key directly encrypts the data application or storage workflow, usually generated per object / file / record group
Signing key signs artifacts, tokens, or messages release/platform team, identity team, or specific security owner
Password-hashing secret / pepper strengthens password verification workflows identity/security function

Important separation rule

Do not use the same key for all purposes.

Separate:

  • encryption keys from signing keys;
  • production keys from non-production keys;
  • customer-data keys from application-secret keys;
  • CI/CD signing keys from runtime service keys.

Envelope encryption

Envelope encryption is the most useful default pattern for cloud and distributed systems.

How it works

  1. Generate a data key (DEK).
  2. Encrypt the data with the DEK.
  3. Encrypt the DEK with a wrapping key / KEK in KMS or HSM.
  4. Store the encrypted DEK next to the ciphertext.
sequenceDiagram participant App participant KMS participant Store App->>KMS: Generate data key / wrap request KMS-->>App: plaintext DEK + encrypted DEK App->>App: Encrypt data with plaintext DEK App->>Store: ciphertext + encrypted DEK App->>App: Zero plaintext DEK from memory

Why this is good

  • data keys can be short-lived and highly scoped;
  • the wrapping key stays in KMS/HSM;
  • rotation becomes more manageable because you can re-wrap keys rather than re-encrypt all data immediately in every case;
  • workloads do not need long-lived plaintext master keys.

Signing vs encryption

Teams often confuse these.

Need Use Why
Keep plaintext secret encryption protects confidentiality
Prove origin / integrity signing protects authenticity and integrity
Detect modification in storage or transit signing or AEAD / MAC ensures tampering is visible
Approve artifacts or releases signing / attestation enables verification before deploy

Design rule

If integrity matters, do not assume confidentiality alone gives it to you.

  • For modern symmetric encryption, use AEAD modes such as AES-GCM or ChaCha20-Poly1305.
  • For artifact, token, or message authenticity, use proper signature or MAC verification.

Who rotates keys?

Rotation is an ownership problem before it is a technical problem.

Asset Typical rotator Notes
KMS/HSM wrapping keys platform/cloud security policy-driven, auditable, usually centralized
app data keys app/service workflow via KMS APIs should be generated automatically, not manually tracked
TLS cert private keys platform / PKI automation / service mesh / ingress team automate issuance and renewal
CI/CD signing keys release engineering or supply-chain security owner guard closely; tie to provenance and verification
password-hashing parameters and peppers identity/security owner rollout must be compatible with auth flows

Rotation rules

  • rotate on schedule and on suspected compromise;
  • test re-encryption / re-wrap / rollback paths before you need them;
  • keep key usage logs and ownership visible;
  • never make rotation somebody else's implied task.

Algorithm choices in 2026

Good default choices now

Use case Practical defaults
symmetric encryption AES-GCM (128/256) or ChaCha20-Poly1305
password hashing Argon2id first; scrypt, bcrypt, or PBKDF2 where constraints require
key agreement / ECDH X25519 or NIST P-256 / P-384 where required
signatures Ed25519, ECDSA P-256/P-384, or RSA-PSS when RSA is still needed
transport security TLS 1.3 with AEAD suites
future-facing crypto agility plan for ML-KEM and ML-DSA adoption paths and hybrid rollouts

Weak / legacy / poor-fit choices

Algorithm / pattern Problem
MD5 broken for collision resistance; not for new security use
SHA-1 collision-broken for signature / integrity designs that rely on collision resistance
DES too small key size
3DES / TDEA legacy-only and retired for modern encryption use
RC4 broken / obsolete
AES-ECB leaks data patterns
RSA < 2048 insufficient security strength for modern designs
RSA PKCS#1 v1.5 key transport legacy / disallowed in current NIST transition guidance
CBC without authentication encryption without robust integrity protection
home-grown crypto almost always a design and implementation trap

What looks promising

For crypto agility and post-quantum preparation, the important 2026 message is not "replace everything tomorrow." It is:

  • keep designs agile;
  • avoid hard-coding one irreversible primitive everywhere;
  • track support for ML-KEM and ML-DSA in your platform stack;
  • prefer managed KMS/HSM services that can absorb algorithm transitions more safely.

Top 5 weak-crypto coding mistakes

1) Weak password hashing

Bad pattern

Using MD5, SHA-1, or plain SHA-256 for password storage.

Why it fails

These are fast hashes and make brute-force and GPU cracking easier.

Better pattern

Use Argon2id or a slow password KDF supported by your platform.

Python

# bad
import hashlib
hashed = hashlib.sha256(password.encode()).hexdigest()

# better (argon2-cffi)
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)

Java

// bad: fast unsalted hash for password storage
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(password.getBytes(StandardCharsets.UTF_8));

// better: PBKDF2 if Argon2 is not available in your stack
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 310000, 256);
byte[] hash2 = f.generateSecret(spec).getEncoded();

PHP

// bad
$hash = sha1($password);

// better
$hash = password_hash($password, PASSWORD_ARGON2ID);

2) Static IV / nonce reuse

Bad pattern

Hard-coding or reusing IVs/nonces, especially with GCM/CTR.

Why it fails

Nonce reuse can destroy confidentiality and, in some modes, integrity.

Python

# bad
iv = b"0000000000000000"

# better
from os import urandom
iv = urandom(12)  # for GCM-style constructions where library expects 96-bit nonce

Java

// bad
byte[] iv = "0000000000000000".getBytes(StandardCharsets.UTF_8);

// better
byte[] iv = new byte[12];
SecureRandom.getInstanceStrong().nextBytes(iv);

PHP

// bad
$iv = str_repeat("A", 12);

// better
$iv = random_bytes(12);

3) ECB mode or unauthenticated CBC

Bad pattern

Using AES/ECB/... or CBC without robust authenticity checks.

Why it fails

ECB leaks structure; CBC alone does not give modern authenticated encryption.

Java

// bad
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");

// better
Cipher c2 = Cipher.getInstance("AES/GCM/NoPadding");

PHP

// bad-ish legacy pattern
$ciphertext = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);

// better use libsodium or an AEAD-capable construction

4) Insecure randomness

Bad pattern

Using random, mt_rand, or java.util.Random for keys, tokens, IVs, or password reset values.

Better pattern

Use CSPRNGs.

Python

# bad
import random
secret = str(random.randint(100000, 999999))

# better
import secrets
secret = secrets.token_urlsafe(32)

Java

// bad
Random r = new Random();
byte[] key = new byte[32];
r.nextBytes(key);

// better
SecureRandom sr = SecureRandom.getInstanceStrong();
sr.nextBytes(key);

PHP

// bad
$token = md5(mt_rand());

// better
$token = bin2hex(random_bytes(32));

5) Disabling verification or doing DIY crypto

Bad pattern

  • skipping certificate validation;
  • accepting any signature/token algorithm;
  • using custom XOR / homemade encryption wrappers.

Why it fails

The code may "work" functionally while quietly removing the trust guarantees you actually need.

Review cues for PRs

Look for:

  • ECB
  • md5( / sha1( for password storage
  • Random() / mt_rand() / random.randint() in security-sensitive code
  • hard-coded IV / nonce / salt
  • verify=False, TrustAll, disabled hostname verification, or custom crypto helpers nobody can explain clearly

AWS and Azure KMS / HSM patterns

For provider-specific implementation detail, see:

References and further reading

  • OWASP Cryptographic Storage Cheat Sheet
  • NIST SP 800-131A Rev. 2
  • TLS 1.3 (RFC 8446)
  • NIST FIPS 203 / 204 / 205 and related PQC materials

Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.