Evaluating Cryptographic Solutions
Within most areas of application security there is a broad range of threat actors who have the capability to exploit vulnerabilities in an application. With cryptography though, as long as the appropriate algorithms are used for their intended purpose and implemented correctly, the range of threat actors is reduced to only well-resourced attackers (typically nation-states). When evaluating cryptographic solutions, I start by answering four basic questions:
- Does the solution use trusted cryptographic primitives?
- Are those primitives implemented correctly?
- Are the primitives used for their intended purpose?
- Is the solution needlessly complex?
If the cryptographic solution I'm evaluating holds up to these questions, I then move on to comparing the solution to it's threat model. If the solution does not hold up to these questions then it is also unlikely it will meet the security requirements of the threat model.
Trusted Cryptographic Primitives
Every cryptographic solution is built on primitives such as cryptographically secure pseudorandom number generators (CSPRNG), hashes, and encryption algorithms. It is vitally important that the primitives used are trusted by the cryptographic community. Cryptography, like much of the rest of application security is a cat and mouse game where new algorithms are created, and new methods of breaking those algorithms are devised. Each iteration leaves us with better algorithms than we had before. Latacora created a great resource for determining the right primitives to use.
Implemented Correctly
Using the correct primitives, if they have been poorly implemented, will also break a cryptographic solution. All cryptographic primitives have well-defined assumptions that must hold for the primitive to meet its security guarantees. If a developer implements the primitive incorrectly the assumptions are broken and therefore the primitive is insecure. Only use cryptographic primitives from trusted libraries such as the one's built into a programming language or ones generally approved by the cryptographic community such as:
When it is not possible to use a trusted library, the crypto-condor suite from Quarkslab can be used for testing cryptographic primitives to ensure they are implemented correctly.
For their Intended Purpose
It is also important that cryptographic primitives are used for their intended purpose. Encryption algorithms should not be used for hashing since hashing is not intended to be reversible. When deriving an encryption key from a low-entropy source like a password, do not use a fast hashing algorithm such as SHA2, SHA3, or Blake2/3, instead use a password-based key derivation function such as Argon2id, scrypt, or bcrypt. Finally, never use a pseudo random number generator (PRNG) for cryptography, always use a CSPRNG. The values produced by a PRNG can be predicted or recovered based on a snapshot of the current state of the PRNG. This predictability makes them unsuitable for use in cryptographic solutions.
Avoiding Needless Complexity
Finally, cryptographic solutions should avoid needless complexity. The solution should be simple enough that there are "obviously no errors." When generating keys, use a CSPRNG to produce enough random bits and encode them if necessary. There is no need to try to manipulate the bits any further as a CSPRNG is already secure for its purpose. When encrypting data, properly use a single algorithm as using multiple algorithms, likely won't provide any extra security and may interact in such a way as to cause the solution to be weaker than if one algorithm was properly used.
Conclusion
Evaluating cryptographic solutions requires an understanding of which primitives are appropriate to use, whether they have been implemented correctly, and whether they are being used for their intended purpose. If you need help evaluating your cryptographic solution, reach out and we can help.