Cryptography Best Practices for Developers

Chapter 1 · Practice 2 of 4

Use a high-level library such as libsodium, Tink or your platform's vetted API

From Choosing the right tool and library

Why it matters

High-level libraries remove the decisions that cause most mistakes. Google's Tink describes itself in terms of primitives defined by the security properties they satisfy, and its AEAD interface does not let callers supply a nonce at all: a new one is generated for every encryption, which rules out nonce reuse by construction.

OptionGood forNotes
libsodium (and bindings such as PyNaCl, sodium-native, libsodium.js)apps that need encryption, signatures, key exchangesmall, opinionated API; XChaCha20-Poly1305, Ed25519, X25519
Tink (Java, Go, Python, C++, Obj-C)teams using cloud KMSkeysets, built-in rotation, KMS envelope encryption
Platform APIs (Web Crypto, .NET, Java JCA, Go crypto)when you must stay on the standard librarylower level: you choose modes and nonces yourself
Protocols (TLS, HPKE, age)data in transit, file encryption, sealed messagesprefer these over hand-built schemes

Low-level APIs are not wrong, but they leave you to choose the mode, nonce size, tag length and key format. If you use them, wrap them in one small internal module with tests and known answer vectors, and let the rest of the codebase call only that module.

Pin library versions, track their security advisories, and avoid unmaintained wrappers that bundle an old copy of the C library.

Example

Python
# PyNaCl (libsodium): authenticated secret-key encryption in two calls
import nacl.secret, nacl.utils

key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)  # 32 bytes from a CSPRNG
box = nacl.secret.SecretBox(key)

ciphertext = box.encrypt(b"card ending 4242")  # random nonce chosen and prepended for you
plaintext = box.decrypt(ciphertext)             # raises CryptoError if tampered with