Skip to content

Instantly share code, notes, and snippets.

Last active April 21, 2024 23:48
Show Gist options
  • Save paragonie-scott/e9319254c8ecbad4f227 to your computer and use it in GitHub Desktop.
Save paragonie-scott/e9319254c8ecbad4f227 to your computer and use it in GitHub Desktop.
An Open Letter to Developers Everywhere (About Cryptography)

Hello software developers,

Please check your code to ensure you're not making one of the following mistakes related to cryptography.

I. General Mistakes

II. Symmetric-key Cryptography Mistakes

III. Asymmetric-key Cryptography Mistakes

If you're looking for cryptography right answers, check out the linked gist.

Thank you for your time.

A friend who wants your application to be secure

Copy link

If you happen to be a PHP developer and aren't sure how to solve your cryptography requirements for your project, I've got you covered there too: secure PHP cryptography library recommendations.

Copy link

Ivoz commented Nov 18, 2015

  • Generating anything but >= 2048 bit RSA keys; accepting <1024 bit RSA keys (<2048 bit for new applications)
  • Accepting <1024 bit DH parameters; Not deploying >= 2048 DH parameters only in new applications
  • Thinking the security benefits of any salt you're using should rely on it being kept secret
  • Using a library written only in a dynamic language for cryptography, this will 99% of the time mean you are now open to side channel attacks (surprisingly easy if you're running virtual hosts in a shared environment)
  • Comparing two cryptography-related values without using a constant-time comparison function
  • Not trying to use the highest-level cryptography API you can possibly get away with (e.g: going straight for OpenSSL instead of libsodium)
  • Demanding all sorts of requirements for passwords - if necessary, just ask for a long one.

Copy link

No need to add it to your article. I'll add this here- there is a single edge case exception for ECB mode- using it with a block cipher as a userspace CSPRNG, such as AES. Of course, the only reason you're writing a userspace CSPRNG is because a kernelspace CSPRNG does not exist (embedded, whatever).

The problems with CBC, OFB, CTR, etc. is that the all rely on other a separate IV or nonce object. This is another "thing that can go wrong" and possibly compromise the security of your block cipher CSPRNG. Instead, ECB mode doesn't need anything of these things- just the keyed data.

As a psuedocode example:

key = <16-bytes of sufficient entropy data>
i = <16-bytes of zero>
while true:
    print AES-ECB(i, key)

This looks a lot like AES-CTR, but doesn't require a nonce to start. The only thing that needs to be provided by the end user, is the key. The security of the userspace CSPRNG rests entirely on the entropy of the key. Further, if the data is only 16-bytes, and never larger, then you'll only be encrypting a single block, and you'll never need to worry about data structure leaks.

Of course, the counter in AES-CTR could be randomly generated by system, but then you have a chicken-and-egg problem. How do you guarantee the entropy of the counter, if you don't have a kernelspace CSPRNG? You could statically set the counter to zero, but then you're no better off than AES-ECB.

Just a thought.

Copy link

I'd really like references to Keyczar, Kalium, cryptlib or libsodium in there (as well as any other high level crypto libraries in there). Blog post:

Also links to and

Copy link

Matir commented Nov 18, 2015


How is your system any different than using CTR mode with the nonce set to 0? This isn't even a "they are similar" comparison: the output should be identical. Both are AES(0, key) + AES(1, key) + ... + AES(n, key)

Copy link


The only argument is simplicity. AES-ECB(data, key) is simpler than AES-CTR(data, key, counter).

Copy link

Please take a look at Thomas Pornin's post on elliptic curves in regard to your last point on safe curves.

Copy link

paj28 commented Nov 19, 2015

I like your post. However, claiming "Not using authenticated encryption" is a mistake is a bit alarmist - "encrypt then MAC" works fine. Also, it might be worth having a separate section for password hashing; Failing to use a salt is another mistake. Personally, I'd remote the "Exception: for the sake of learning" because that actually applies to everything here, not just home-grown ciphers.

Copy link

creshal commented Nov 19, 2015

With the PHC over, I'd recommend adding its Argon2 password hashing together with bcrypt.

Copy link

@creshal I wouldn't. Argon2 is new and given the suite of current algorithms, relatively untested and unused. I would let Argon2 bake for at least 5 years before giving it considerable attention for production use. We need mathematicians and cryptographers analyzing Argon2 for a good long while, before it becomes an every day recommendation. In the meantime, bcrypt is a sound choice.

Copy link

You'll be spared all these problems of home grown solutions if you use standard JOSE and JWT, elegant crypto designed primarily for web and mobile apps :) JOSE stands for JavaScript Object Signing and Encryption, JWT for JSON Web Tokens. For Java and Android check out the Nimbus JWT lib:

Copy link

@vdzhuvinov there is nothing "elegant" about JOSE and JWT. They had the opportunity to greenfield, but ended up effectively creating JSON-ized SAML full of design mistakes which really shouldn't exist if they had properly studied the attacks on SAML:

  • alg is mandatory for tokens but not for keys: this specifically lead to this class of vulnerabilities which shouldn't exist in modern standards: This is practically backwards: alg should be mandatory for keys, and tokens should carry only a kid to associate a token with a particular key, and NEVER carry an alg field (instead kid is optional and alg is mandatory for tokens). This way, the verifier always knows the algorithm, and doesn't need to consult the (malleable) part of the token to infer the algorithm. That's just asking for attacks which trick the verifier into using the wrong algorithm (see earlier mentioned link).
  • Transferring claims between multiple principals (i.e. 3+ principal interactions) are a traditionally difficult problem that leads to ambiguity and confusion (i.e. "confused deputy problem"). With SAML we saw confusion between claims for different principals being misused in the wrong context, "fixed" in SAML 2.0 with AudienceRestrictions. This was carried over in JWT as the aud attribute, but it could've been solved with a cryptographic binding between principals that absolutely ensures claims can't be confused (as seen in e.g. Macaroons). Instead JWT merely carried over SAML's half-solution, and worse, they chose not to make it mandatory, meaning it will not necessarily be automatically checked, so we have two more sharp edges: failure to include an aud attribute leading to attributes being confused in different contexts, and a potential failure to check aud even if it's present. Macaroons, on the other hand, not only cryptographically bind credentials acquired by multiple principals together (i.e. third party caveats / "holder-of-key proofs"), but provide a formal authorization logic for proving them correct.
  • JOSE and COSE did not specify signatures in a way that's interoperable. This is not a problem with other bearer credential formats (e.g. Macaroons) where the tokens can be transcoded between serialization formats and signatures will still verify. COSE couldn't do this due to a lack of forethought on JOSE's part. Anyone who is using JOSE now who wants to transition to COSE must make a wholesale switch: they can't incrementally upgrade.

JOSE is an ugly, design-by-committee standard. It is an unopinionated, one-size-fits-all solution. About the best I can say is that JWE and JWS are better than CMS, but that's not saying much.

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