Quite often, formal education in cryptography focuses on the mathematics and theory of cryptographic algorithms. While this is important, it is not where real-world attacks generally occur. It is important for those designing or using cryptographic systems to understand where those real-world attacks do occur, so they can take measures to block the threats in their systems.
What are the real-world attack vectors?
The first reaction many people would have to this question is that attacks break cryptographic algorithms, or the cryptographic protocols that use them. However, these are rarely the places successful attacks occur in real systems. Instead, the real attacks occur through things like these:
- Misusing keys or cryptographic API functions
- Insider attacks because you “trust your own employees”
- Unauthorized access to systems and functions
- Exploiting security flaws in software
- Allowing malware into the system
The lesson to be learned here is that people do not break the cryptographic algorithms – instead, they use attacks that simply bypass them. An attacker will use the easiest method available to achieve their goal, and breaking a cryptographic algorithm is almost never the easiest method.
Consider the following quote from Eugene Spafford: “Using encryption on the Internet is the equivalent of arranging an armored car to deliver credit card information from someone living in a cardboard box to someone living on a park bench.”
Here are some important questions to ask yourself.
What use is a great cryptographic algorithm if I can
Get your keys?
Use your keys without needing to know their value?
Get your data when it’s not encrypted?
Use combinations of “secure” functions to decrypt data or keys?
Impersonate you and do everything you’re permitted to do?
Let’s take a closer look at some of these problems, and how to solve them.
Approaches to solving the big problems
As we’ve seen, it is not enough to just use strong cryptographic algorithms. To address the problems we mentioned above, you need two things.
You need a Cryptographic Module
From a black box standpoint, a Cryptographic Module can be viewed as shown in the figure below.
The module can be asked to perform various functions by sending requests to perform API functions. Those API functions may invoke cryptographic algorithms that are implemented within the module, and they may use cryptographic keys that are secured within the module. The API functions are typically more complex than simply invoking a particular cryptographic algorithm, and one important aspect is that all intermediate results within a complex operation are protected against exposure outside the module. We’ll talk a bit more about those API functions later.
The Cryptographic Module has a set of important characteristics.
- It may be either hardware or software based, depending on the level of security you require. Modules implemented in hardware are generally more secure than those implemented in software.
- Sensitive data, including critical keys, is protected by the module.
- Access is only through carefully designed APIs. Use of the API functions is access controlled.
- The function of the module cannot be changed without authorization. For example, new firmware cannot be loaded into a hardware-based cryptographic module except by authorized people.
As you can see in the figure above, every Cryptographic Module is surrounded by a Module Boundary. This may be a physical boundary, in the case of a hardware-based module – or it may be a logical boundary in the case of a software module. A logical boundary might be implemented using features such as separate processes, virtual machines, containers, or other features available in software and operating systems. The module boundary is an essential component of the security of the module. It ensures that no attacker can either alter the functions performed in the cryptographic subsystem, nor view or alter sensitive data like keys.
You need a well-designed Cryptographic Architecture
The cryptographic module implements a Cryptographic Architecture. This defines such things as:
- A carefully-designed set of API functions, which are the only way applications outside the module can ask it to perform any operations.
- A method of protecting cryptographic keys. This includes ensuring secrecy and integrity of the keys, but it may include other features, like control over the set of operations for which each key may be used.
- Access control, which is used to control which module functions an external user or application will be permitted to use.
The module will reliably implement the cryptographic architecture, even in the face of motivated attackers. This is a result of two factors described above: the limitation that it can only be accessed through its API functions, and the fact that the module boundary ensures it will be a “black box” that cannot be altered or probed.
Securing keys and controlling their use
Once you have a cryptographic module, there are two extremely important requirements for security of the cryptographic keys.
The first is that keys must never appear in cleartext (unencrypted form) outside of the cryptographic module. In other words, unencrypted keys must never be available to application programs, the operating system, or anything else outside the module boundary. This requires very careful design of the module’s API functions and cryptographic architecture. It also requires that at least one key, often called a Master Key, must reside inside the cryptographic module. Keys that are outside the module and in encrypted form have to be encrypted using some other key, and the only possibilities are to use another encrypted key from outside the module, or an unencrypted key that is stored inside the module. The key hierarchy must have at least one top-level key that is not in encrypted form, and the only way to achieve security is for that key to be protected within the cryptographic module boundary.
The second requirement is that each key must have a set of attributes securely bound to it. As we mentioned above, most systems include control over the operations for which a key may be used. This control is implemented through attributes that are bound to the key. The cryptographic module API functions examine the attributes for each key before allowing its use, ensuring the key is acceptable for the requested function. The binding process must be such that it is impossible to use the key if the attributes are altered. The attributes may be simple, or they may be quite complex. Typically, they control things like these:
- The cryptographic algorithm with which the key can be used
- The mode of operation for which the key can be used, e.g. CBC encryption, Counter Mode encryption, CMAC MAC generation, etc.
- The direction of operations permitted for the key, e.g. encryption vs. decryption or MAC generation vs. MAC verification
- Application-level usage permitted for the key, e.g. data encryption, key wrapping, base key for key derivation, message authentication, etc.
- Whether the key can be exported from this system
Let’s look at a simple example. Say that an application asks the cryptographic module to encrypt some data in AES CBC mode using a key it provides as one of the API parameters. The API function within the cryptographic module would perform steps like these before using the key:
- Check the integrity of the key, to make sure it was not accidentally or maliciously altered.
- See if the attributes indicate the key is to be used with the AES algorithm.
- See if the attributes indicate the key is to be used in CBC mode.
- See if the attributes indicate the key is allowed to perform encryption (as opposed to a key only allowed to perform decryption).
What kinds of problems do the attributes prevent? Here are a few examples.
- Prevent using a key-encrypting key in a decryption operation, to decrypt a wrapped key and obtain its cleartext. The attributes for the key-encrypting key would only allow its use in key management functions, and block its use in a generic decryption function.
- Prevent someone from impersonating you by being able to generate a MAC when they should only be able to verify MACs you generated. You would have a copy of the MAC key with attributes that allow generation of MACs, but other people would have a copy of that same key with attributes that only allow them to verify MACs.
- Prevent decrypting a PIN by using a PIN key in a “decipher” operation. Like the key-encrypting key example above, this prevents using a PIN key in a generic decryption function by attaching attributes that limit it to use in PIN-related functions.
- Prevent breaking a key by using it with an algorithm weaker than the intended one. For example, it would be easier to break a 128-bit AES key if you can use it as a double-length Triple-DES key. Attributes that define the algorithm the key can be used with prevent this problem.
- Prevent export of a key to another party, when the key was intended for use only within your own organization. Attributes that define exportability of a key can prevent its use by functions that would wrap it under a key shared with another party.
The attributes are embedded in a structure that is cryptographically bound to the key itself in a way that ensures the key cannot be used if the attributes are modified in an unauthorized way.
Managing your keys
It may seem obvious that you need to keep your keys secure, but the attacks and countermeasures are not always so obvious. Let’s take a look at some important issues.
You need secure ways of getting keys into your system
You must carefully control who can enter keys into your system, particularly keys with known values. This includes operations like the ones shown in the figure below:
Consider this scenario: Someone enters a key-encrypting key (KEK) into your system with a value they choose. They could then use that KEK to encrypt your other keys, as if they were exporting those keys to another party using a valid KEK. Once they have your keys encrypted with their known KEK, it is a simple matter to decrypt all of your keys (perhaps on their own PC) and obtain their values – allowing them to attack many things in your system. So, what do you do to prevent this?
The most obvious solution is to control who can execute the functions to enter known-value keys. However, even this is not enough. Attacks like this are so powerful that a valid user might be bribed or blackmailed to execute the attack, or they might not be able to resist doing it on their own for financial gain or to harm your system.
Instead, what you need to do is design the system so two or more people must collude to mount this attack. This means that you must use dual-control, split-knowledge techniques. But what are those? the dual-control part means that two separate people must each take action to load a known-value key into your system. The split-knowledge part means that each of those people have a piece of data that is used in creating the key in your system, but the data gives nothing away about the value of the key until all pieces are combined – which is done securely inside your cryptographic module.
There’s a good, general principle here: Don’t ever allow any single individual to have the authority to do things that circumvent your security.
You need ways to securely exchange keys with other people
Much of the time, you use cryptography to protect information you exchange with other people or systems. If you use symmetric key cryptography, you and the other party need to have the same cryptographic key – but how do you do that while ensuring nobody else could get the key? You need to ask yourself questions like these:
- How do you send them your keys securely?
- When you receive a key, how do you know it really came from them, and not an imposter?
- How do you verify you received a key with the correct value, and not one that was corrupted?
Sending keys securely, of course, means that those keys must be encrypted – but that raises the key management issues mentioned above, where you must ensure no one but the other intended party can get the KEK. Knowing that a key came from the expected party is an authentication process, and there are several cryptographic techniques that can be used to provide authentication.
Finally, you can verify that the key you received is correct (not accidentally corrupted or maliciously replaced) by computing a Key Check Value (KCV) on the key. A KCV is a cryptographically-computed “checksum” on the key, and the KCV algorithms are designed to ensure that the KCV itself reveals nothing about the value of the key. Whoever created the key can give you the KCV in advance of the time you receiving the encrypted key itself.
How do you back up your cryptographic keys?
Every system needs a backup process, so that data can be restored in case of equipment failure, accidental deletion, ransomware attacks, or other problems. However, you need to take special care when backing up cryptographic keys, in order to keep them secure. An attacker who can get to your backup media must not be able to use it to learn the values of your keys. This generally means that you must use a two-layered approach to backing up your keys:
- The keys on the backup medium must be in encrypted (wrapped) form, and that medium must not contain the wrapping keys needed to decrypt those backed-up keys.
- The top-level wrapping keys must be securely stored somewhere other than the backup medium. For example, they might be stored using the dual-control, split-knowledge technique mentioned earlier, or they might be stored on smart cards that are locked in a safe.
What happens when you want to stop using a key?
This is called key retirement. Why would you retire a key? There are several reasons. It might simply be a key you no longer need. It might be a key you have replaced with a newer key, in a key rotation process. It might be a key that has been compromised, where you know or suspect that an attacker now knows the key.
When a key is retired, you stop using that key on your system. However, this is not as straightforward as it sounds. For example, you need to ensure that all copies of the key cease being used, wherever they may be and whoever may have them. You may have shared the key with a partner for use in transactions, you may have backup copies, etc. Another aspect is that you may want to stop using a key to create new content, but continue using it for some time to process existing content – for example, to decrypt previously-encrypted data or to verify message authentication codes (MACs) that were computed using that key. These things require carefully-designed processes and good control.
Compromised keys are a special case. It is usually urgent to stop using those keys, but that means you must replace them and coordinate with all other parties who have that key. To be able to do this quickly, you must plan in advance what you will do in case of a key compromise, so you are ready and have the processes in place.
Defining secure API functions for the cryptographic module
Remember those API functions we mentioned earlier? You can view them at a high level as shown in this figure:
The point here is that you send a request to the cryptographic module and it does a lot of things before returning – which might include multiple calls to cryptographic algorithms, use of internally-stored keys, manipulation of data between operations, etc. These multiple steps often involve intermediate results that would impact security if they could be seen, or if they could be manipulated. Thus, rule #1 for secure API functions is to design them as atomic functions where no sensitive data is exposed outside the cryptographic module.
The second thing to consider is that your design must prevent the misuse of combinations of APIs to expose data. It is easy to design an API function that is secure by itself – but it is much harder to ensure that multiple API functions cannot be used in unexpected combinations to circumvent security. You must think about all the outputs of your API functions, and what happens if they are used as inputs to other, seemingly unrelated API functions. This is very important, but very difficult to do.
Finally, all API functions must verify the keys they receive as parameters, and must enforce any attributes attached to those keys. The API function must first verify the integrity of the key, then verify that its attributes allow it to be used for the requested purpose. It is essential that this is enforced internally by the API functions of the module.
What we’ve seen here is that a secure cryptographic system needs a lot more than just some good algorithms. The weaknesses attackers will actually exploit are not in the algorithms themselves, but are in the other layers of the system surrounding the algorithms.
There is a lot of subtlety and complexity in the countermeasures you must have to defeat dedicated and capable attackers. Thus, this is an area best handled by experienced experts. However, you can develop your own expertise, and if you are interested, I encourage you to find the people and resources that can set you on that path.