Skip to content

Instantly share code, notes, and snippets.

@atoponce
Forked from tqbf/gist:be58d2d39690c3b366ad
Last active October 12, 2025 19:54
Show Gist options
  • Save atoponce/07d8d4c833873be2f68c34f9afc5a78a to your computer and use it in GitHub Desktop.
Save atoponce/07d8d4c833873be2f68c34f9afc5a78a to your computer and use it in GitHub Desktop.
Cryptographic Best Practices

Symmetric Encryption

The only way to encrypt today is authenticated encryption, or "AEAD". ChaCha20-Poly1305 is faster in software than AES-GCM. AES-GCM will be faster than ChaCha20-Poly1305 with AES-NI. Poly1305 is also easier than GCM for library designers to implement safely. AES-GCM is the industry standard.

Use, in order of preference:

  1. The NaCl/libsodium default
  2. Chacha20-Poly1305
  3. AES-GCM

Avoid:

  1. AES-CBC, AES-CTR by itself
  2. Block ciphers with 64-bit blocks such as Blowfish
  3. OFB mode
  4. RC4, which is comically broken

Symmetric Key Length

See The Physics of Brute Force to understand why 256-bit keys is more than sufficient. But rememeber: your AES key is far less likely to be broken than your public key pair, so the latter key size should be larger if you're going to obsess about this.

Use:

  1. Minimum- 128-bit keys
  2. Maximum- 256-bit keys

Avoid:

  1. Constructions with huge keys
  2. Cipher "cascades"
  3. Key sizes under 128 bits

Symmetric Signatures

If you're authenticating but not encrypting, as with API requests, don't do anything complicated. There is a class of crypto implementation bugs that arises from how you feed data to your MAC, so, if you're designing a new system from scratch, Google "crypto canonicalization bugs". Also, use a secure compare function.

Use: HMAC

Avoid:

  1. HMAC-MD5
  2. HMAC-SHA1
  3. Custom "keyed hash" constructions
  4. Complex polynomial MACs
  5. Encrypted hashes
  6. Anything CRC

Hashing/HMAC Algorithm

If you can get away with it you want to use hashing algorithms that truncate their output and sidesteps length extension attacks. Meanwhile: it's less likely that you'll upgrade from SHA-2 to SHA-3 than it is that you'll upgrade from SHA-2 to BLAKE2, which is faster than SHA-3, and SHA-2 looks great right now, so get comfortable and cuddly with SHA-2.

Use, in order of preference:

  1. HMAC-SHA-512/256
  2. HMAC-SHA-512/224
  3. HMAC-SHA-384
  4. HMAC-SHA-224
  5. HMAC-SHA-512
  6. HMAC-SHA-256

Alternately, use in order of preference:

  1. BLAKE2
  2. SHA3-512
  3. SHA3-256

Avoid:

  1. HMAC-SHA-1
  2. HMAC-MD5
  3. MD6
  4. EDON-R

Random IDs

When creating random IDs, numbers, URLs, nonces, initialization vectors, or anything that is random, then you should always use /dev/urandom.

Use: /dev/urandom

Create: 256-bit random numbers

Avoid:

  1. Userspace random number generators
  2. /dev/random

Password Hashing

I don't recommend using scrypt for password hashing. It is very sensitive to the parameters, making it possible to end up weaker than bcrypt, and suffers from time-memory trade-off (source #1 and source #2). When using bcrypt, make sure to use the following algorithm to prevent the leading NULL byte problem and the 72-character password limit:

bcrypt(base64(sha-512(password)))

I'd wait a few years, until 2020 or so, before implementing any of the Password Hashing Competition candidates, such as Argon2. They just haven't had the time to mature yet.

Use, in order of preference:

  1. bcrypt
  2. sha512crypt
  3. sha256crypt
  4. PBKDF2

Avoid:

  1. Plaintext
  2. Naked SHA-2, SHA-1, MD5
  3. Complex homebrew algorithms
  4. Any encryption algorithm

Asymmetric Encryption

It's time to stop using vanilla RSA, and start using NaCl/libsodium. Of all the cryptographic "best practices", this is the one you're least likely to get right on your own. NaCl/libsodium has been designed to prevent you from making stupid mistakes, it's highly favored among the cryptographic community, and focuses on modern, highly secure cryptographic primitives.

It's time to start using ECC. Here are several reasons you should stop using RSA and switch to elliptic curve software:

  • Progress in attacking RSA --- really, all the classic multiplicative group primitives, including DH and DSA and presumably ElGamal --- is proceeding faster than progress against elliptic curve.
  • RSA (and DH) drag you towards "backwards compatibility" (ie: downgrade-attack compatibility) with insecure systems. Elliptic curve schemes generally don't need to be vigilant about accidentally accepting 768-bit parameters.
  • RSA begs implementors to encrypt directly with its public key primitive, which is usually not what you want to do: not only does accidentally designing with RSA encryption usually forfeit forward-secrecy, but it also exposes you to new classes of implementation bugs. Elliptic curve systems don't promote this particular foot-gun.
  • The weight of correctness/safety in elliptic curve systems falls primarily on cryptographers, who must provide a set of curve parameters optimized for security at a particular performance level; once that happens, there aren't many knobs for implementors to turn that can subvert security. The opposite is true in RSA. Even if you use RSA-OAEP, there are additional parameters to supply and things you have to know to get right.

If you have to use RSA, do use RSA-OAEP. But don't use RSA. Use ECC.

Use: NaCl/libsodium

Avoid:

  1. RSA-PKCS1v15
  2. RSAES-OAEP
  3. RSASSA-PSS with MGFI-256,
  4. Really, anything RSA
  5. ElGamal
  6. OpenPGP, OpenSSL, BouncyCastle, etc.

Asymmetric Key Length

As with symmetric encryption, asymmetric encryption key length is a vital security parameter. Academic, private, and government organizations provide different recommendations ath mathematical formulas to approimate the minimum key size requirement for security. See BlueKcrypt's Cryptographyc Key Length Recommendation for other recommendations and dates.

To protect data up through 2020, it is recommended to meet the minimum requirements for asymmetric key lengths:

Method RSA ECC D-H Key D-H Group
Lenstra/Verheul 1881 161 151 1881
Lenstra Updated 1387 163 163 1387
ECRYPT II 1776 192 192 1776
NIST 2048 224 224 2048
ANSSI 2048 200 200 2048
BSI 3072 256 256 3072

See also the NSA Fact Sheet Suite B Cryptography and RFC 3766 for additional recommendations and math algorithms for calculating strengths based on calendar year.

Personally, I don't see any problem with using 2048-bit factoring modulus and 256-bit. So, my recommendation would be:

Use:

  1. 256-bit minimum for ECC/DH Keys
  2. 2048-bit minimum for RSA/DH Group

Avoid: Not following the above recommendations.

Asymmetric Signatures

In the last few years there has been a major shift away from conventional DSA signatures and towards misuse-resistent "deterministic" signature schemes, of which EdDSA and RFC6979 are the best examples. You can think of these schemes as "user-proofed" responses to the Playstation 3 ECDSA flaw, in which reuse of a random number leaked secret keys. Use deterministic signatures in preference to any other signature scheme.

Use, in order of preference:

  1. NaCl/libsodium
  2. Ed25519
  3. RFC6979 (deterministic DSA/ECDSA)

Avoid:

  1. RSA-PKCS1v15
  2. RSASSA-PSS with MGF1+SHA256
  3. Really, anything RSA
  4. Vanilla ECDSA
  5. Vanilla DSA

Diffie-Hellman

This is the trickiest one. Here is roughly the set of considerations:

  • If you can just use Nacl, use Nacl. You don't even have to care what Nacl does.
  • If you can use a very trustworthy library, use Curve25519; it's the modern ECDH curve with the best software support and the most analysis. People really beat the crap out of Curve25519 when they tried to get it standardized for TLS. There are stronger curves, but none supported as well as Curve25519.
  • But don't implement Curve25519 yourself or port the C code for it.
  • If you can't use a very trustworthy library for ECDH but can for DH, use DH-2048 with a standard 2048 bit group, like Colin says, but only if you can hardcode the DH parameters.
  • But don't use conventional DH if you need to negotiate parameters or interoperate with other implementations.
  • If you have to do handshake negotiation or interoperate with older software, consider using NIST P-256, which has very widespread software support. Hardcoded-param DH-2048 is safer than NIST P-256, but NIST P-256 is safer than negotiated DH. But only if you have very trustworthy library support, because NIST P-256 has some pitfalls. P-256 is probably the safest of the NIST curves; don't go down to -224. Isn't crypto fun?
  • If your threat model is criminals, prefer DH-1024 to sketchy curve libraries. If your threat model is governments, prefer sketchy curve libraries to DH-1024. But come on, find a way to one of the previous recommendations.

It sucks that DH (really, "key agreement") is such an important crypto building block, but it is.

Use, in order of preference:

  1. NaCl/libsodium
  2. 2048-bit Diffie-Hellman Group #14

Avoid:

  1. conventional DH
  2. SRP
  3. J-PAKE
  4. Handshakes and negotiation
  5. Elaborate key negotiation schemes that only use block ciphers
  6. srand(time())

Website security

By "website security", we mean "the library you use to make your web server speak HTTPS". Believe it or not, OpenSSL is still probably the right decision here, if you can't just delegate this to Amazon and use HTTPS elastic load balancers, which makes this their problem not yours.

Use:

  • OpenSSL, LibreSSL, or BoringSSL if you run your own site
  • Amazon AWS Elastic Load Balancing if Amazon does

Avoid:

  1. PolarSSL
  2. GnuTLS
  3. MatrixSSL

Client-server application security

What happens when you design your own custom RSA protocol is that 1-18 months afterwards, hopefully sooner but often later, you discover that you made a mistake and your protocol had virtually no security. A good example is Salt Stack. Salt managed to deploy e=1 RSA.

It seems a little crazy to recommend TLS given its recent history:

  • The Logjam DH negotiation attack
  • The FREAK export cipher attack
  • The POODLE CBC oracle attack
  • The RC4 fiasco
  • The CRIME compression attack
  • The Lucky13 CBC padding oracle timing attack
  • The BEAST CBC chained IV attack
  • Heartbleed
  • Renegotiation
  • Triple Handshakes
  • Compromised CAs

Here's why you should still use TLS for your custom transport problem:

  • Many of these attacks only work against browsers, because they rely on the victim accepting and executing attacker-controlled Javascript in order to generate repeated known/chosen plaintexts.
  • Most of these attacks can be mitigated by hardcoding TLS 1.2+, ECDHE and AES-GCM. That sounds tricky, and it is, but it's less tricky than designing your own transport protocol with ECDHE and AES-GCM!
  • In a custom transport scenario, you don't need to depend on CAs: you can self-sign a certificate and ship it with your code, just like Colin suggests you do with RSA keys.

Use: TLS

Avoid:

  1. Designing your own encrypted transport, which is a genuinely hard engineering problem;
  2. Using TLS but in a default configuration, like, with "curl"
  3. Using "curl"
  4. IPSEC

Online backups

Of course, you should host your own backups in house. The best security is the security where others just don't get access to your data. There are many tools to do this, all of which should be using OpenSSH or TLS for the transport. If using an online backup service, use Tarsnap. It's withstood the test of time.

Use: Tarsnap

Avoid:

  1. Google
  2. Apple
  3. Microsoft
  4. Dropbox
  5. Amazon S3
@theintz
Copy link

theintz commented Jan 31, 2018

This is actually a very helpful compilation, thanks a lot! Only thing I am missing badly are some sources or references.

@NullVoxPopuli
Copy link

should it be noted that for asymmetric encryption of long messages that you actually need to combine with a symmetric encryption method?

@atoponce
Copy link
Author

@theintz I've just updated the document with many references. More will come in future revisions.

@NullVoxPopuli Yes, what you're describing is "hybrid encryption", where both asymmetric and symmetric encryption primitives are deployed. In this scenario, a symmetric key is randomly generated, which then using a symmetric algorithm, like AES, to encrypt the payload. Then the asymmetric public key is used to encrypt the symmetric AES key. The encrypted key and encrypted payload are sent to the owner of the asymmetric private key. If you're using NaCl, libsodium, or monocypher, this is all builtin.

@emad7105
Copy link

emad7105 commented May 21, 2018

@atoponce First of all, thanks for the nice compilation.

We develop polyglot software systems using various programming languages, especially while we're developing a microservice architecture. There is no wonder or doubt that libraries such as NaCl are there to encapsulate and abstract cryptographic complexities for software developers with minimal cryptographic background. You emphasise that NaCl, libsodium and monocypher are better libraries compared to, for example, BouncyCastle.

What to do with a piece of software being developed using Ruby, Javascript, Java or a .Net Framework language (etc.)? I do not advocate these programming languages, but as a matter of fact, these languages have been used and definitely will be used everywhere. I am not sure whether one can convince all software development teams to use native interfaces. What are your thoughts on other cryptographic libraries? Any recommendations?

Again, thanks for the nice compilation.

@PlanetRenox-zz
Copy link

PlanetRenox-zz commented May 31, 2018

I see a problem here, in the password hashing section the following algorithm is recommended for bcrypt:

bcrypt(base64(sha-512(password)))

bcrypt has a limit set at 72 bytes as you mention but running base64 on sha-512 results in 88 bytes.
Are you suggesting we cut the hash instead of using sha-256 or 384? or i assume this is a mistake.

@juan88
Copy link

juan88 commented Jun 25, 2018

Excellent guide!

It would be nice to have recommendations as where to store keys for the case of symmetric algorithms that are used to encrypt data.

@mockturtl
Copy link

mockturtl commented Sep 23, 2018

Possibly of interest: Miscreant

Highlight:

Repeated use of the same nonce under the same key causes most ciphers to fail catastrophically... repeating a nonce under AES-GCM leaks the cipher’s authentication key, allowing an attacker to perpetrate chosen ciphertext attacks including message forgeries and even potentially full plaintext recovery. The XSalsa20Poly1305 and ChaCha20Poly1305 constructions, found in NaCl-family libraries such as libsodium, fail in a similarly spectacular way

Ciphers with nonce reuse misuse resistance, such as the AES-SIV and AES-PMAC-SIV ciphers provided by this library, do not

@AGuyNamedTal
Copy link

Really good and helpful guide! What would you say about what to use when implementing end to end encryption (for example like in chat applications)?

@johnnyRose
Copy link

FYI, the Keybase filesystem now provides 250GB for free instead of 10: https://keybase.io/docs/kbfs

@rt5v3y02fk
Copy link

We have a crypto best practices web site https://CryptoDoneRight.org. If you want to be a contributor to the site then either let me know or just submit a feedback form to the site directly. We are looking for more contributors to help build out the crypto knowledge base.

@BlackJar72
Copy link

Any advice on the legality and safety ethics of hobby cryptography -- or the sharing / open sourcing of (original) cryptographic algorithms? (Not really security related.)

@rm-rf-etc
Copy link

rm-rf-etc commented Dec 4, 2019

@NullVoxPopuli If you're using NaCl, libsodium, or monocypher, this is all builtin.

I think there's an important distinction between "builtin" and "included". Please correct me if I'm wrong, but symmetric and asymmetric encryption are each handled with different methods. This to me would be more like "included", whereas "builtin" would be more like both functioning thru the same methods, but everything happens automatically with nothing needing to be done by the library author (me).

The Rust port of libsodium, sodiumoxide, has the best / simplest / most straightforward description that I've seen:

For most users, if you want public-key (asymmetric) cryptography you should use the functions in crypto::box_ for encryption/decryption.
If you want secret-key (symmetric) cryptography you should be using the functions in crypto::secretbox for encryption/decryption.
For public-key signatures you should use the functions in crypto::sign for signature creation and verification.

Libsodium in the browser

Anyone wanting to use npm libsodium-wrappers for speedy javascript encryption:

Asymmetric

https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption
-> sodium.crypto_box_easy
-> sodium.crypto_box_open_easy

Symmetric

https://libsodium.gitbook.io/doc/secret-key_cryptography/secretbox
-> sodium.crypto_secretbox_easy
-> sodium.crypto_secretbox_open_easy

@robchristian
Copy link

Looks like I was wrong, symmetric encryption is built into the public-key encryption?

Another virtue of NaCl's high-level API is that it is not tied to the traditional hash-sign-encrypt-etc. hybrid structure. NaCl supports much faster message-boxing solutions that reuse Diffie-Hellman shared secrets for any number of messages between the same parties.
https://nacl.cr.yp.to/features.html

@Raincode
Copy link

Raincode commented Feb 23, 2022

Thank you for this!

I would like CMAC a.k.a. OMAC1 (or OMAC in general) mentioned in the MAC section, and CBC-MAC as don't or at least noting the pitfall(s).

@maxgorovenko
Copy link

I got little misunderstanding about Asymmetric Signatures.

The author says:

Ed25519, the NaCl default, is by far the most popular public key signature scheme outside of Bitcoin.

But NaCL on their website ( https://nacl.cr.yp.to/sign.html ) says:

This signature software (both at the C level and at the C++ level) is a prototype. It will be replaced by the final system Ed25519 in future NaCl releases.

I'm writing software license checking subsystem with GoLang and going to use asymmetric signatures for that purpose. I can use directly ed25519 or use it through NaCL, which actually should be it's fork - libsodium (its said here https://pkg.go.dev/golang.org/x/[email protected]/nacl/sign ).

As far as I'm not a crypto expert, I'm wondering what should I choose.

@Raincode
Copy link

@maxgorovenko I think what it means on the NaCl site is the implementation of Ed25519 itself will be swapped. NaCl sign uses Ed25519 by default, see "Selected primitive" and "Alternate primitives". I'd go for the most abstract function, so libsodium sign or similar should be a great choice. Especially if you don't care really about the underlying algorithm and just want the crypto "done right", a good library with high level of abstraction is your best bet to minimize room for errors.

@Raincode
Copy link

Looks like I was wrong, symmetric encryption is built into the public-key encryption?

Another virtue of NaCl's high-level API is that it is not tied to the traditional hash-sign-encrypt-etc. hybrid structure. NaCl supports much faster message-boxing solutions that reuse Diffie-Hellman shared secrets for any number of messages between the same parties.
https://nacl.cr.yp.to/features.html

Mmh... I'm also a bit confused. My best bet is, what they are referring to, when you want to exchange several messages securely between two parties, instead of doing the whole AES keygen, encrypt, asymmetric sign, asymmetric encrypt, etc., they perform a session key establishment via authenticated DH and then use the secret key for repeatedly sending Encrypted+MAC'd messages, which would require only one-time asymmetric operation and efficient symmetric operations for each exchanged message.

@gmhafiz
Copy link

gmhafiz commented Apr 17, 2023

Pbkdf2 number of rounds has been increased to 600,000 by owasp https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

Also argon2id is recommended instead.

@jgowdy-godaddy
Copy link

I'm a big fan of DJB and ChaCha20, but given that almost every common hardware platform and exascale cloud provider supports AES-NI or other AES specific acceleration, why do you preference ChaCha20 over AES-GCM? If you're recommending NaCl, libsodium, monocypher, OpenSSL, or other well established implementations, I'm not sure the "sharp edges" concern about GCM really justifies not using a hardware accelerated solution.

Other than that, great list, thank you for publishing this.

@midn1
Copy link

midn1 commented Apr 17, 2023

This means avoid LibreSSL, BoringSSL, or BearSSL for the time being. Not because they're bad, but because OpenSSL really is the Right Answer here. Just keep it simple; use OpenSSL.

Such things are nonsense and make me skeptical of the rest of this writing. Likewise with the NaCl shilling. If NaCl is made such that you needn't care, then that's a reason to not use NaCl. Not sure what I expected from a pop cryptographer, however.

@jgowdy-godaddy
Copy link

Not sure what I expected from a pop cryptographer, however.

Oof. The rest of the comment / feedback would have stood on its own without adding this bit of toxicity at the end. Maybe work on your professionalism a bit.

@dhenson02
Copy link

Would like to get Bruce Schneier's opinion on this

Anyone got his number?

@q2dg
Copy link

q2dg commented Jul 17, 2023

Yescrypt is used by default in many modern distros for password hashing but it's not even mentioned here....

@rt5v3y02fk
Copy link

rt5v3y02fk commented Jul 17, 2023

This is great. Can we add this to cryptodoneright.org site with the references? Glad to hear about yescrpyt. I’d only heard of scrypt

@oldgalileo
Copy link

oldgalileo commented Oct 11, 2025

Mentioning PASETO vs. JWTs (and pushing readers towards PASETO, or even just away from JWTs) feels like a natural fit here. JWTs have many footguns that I think PASETO (especially v4) addresses quite thoughtfully. Such a recommendation feels in line with "just use NaCL" and "avoid rolling your own key exchange if you can", i.e. use a tool that takes the gun out of your hands.

For reference: https://github.com/paseto-standard/paseto-spec

Interested in your thoughts @atoponce

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment