left-icon

Application Security in .NET Succinctly®
by Stan Drapkin

Previous
Chapter

of
A
A
A

CHAPTER 7

Symmetric Encryption

Symmetric Encryption


Symmetric encryption uses the same secret key to hide and later reveal (encrypt and decrypt) a secret message. Given such a simple concept, most frameworks—.NET included—fail to provide symmetric encryption APIs that are simple, foolproof (i.e. secure and resist misconfiguration and abuse), and useable by engineers (not cryptographers) who have a requirement to encrypt and decrypt data based on a shared secret key.

What most frameworks do provide, however, is a wide assortment of cryptographic primitives, which could be used to construct a secure symmetric encryption API, but are in reality used to create broken, insecure implementations that provide a false sense of security (until your company’s data makes the news). Microsoft is reinforcing this false confidence by claiming the following: “You do not need to be an expert in cryptography to use [System.Security.Cryptography] classes.” This is like saying “You don’t need to know the driving rules because we’ve given you a car.” Standardization bodies such as NIST are part of the problem because they produce standards for cryptographers—not engineers—which the aforementioned frameworks implement and expose to the world.

Imagine NIST telling you that they have a great car for you, called AES. It is secure, approved, time-tested, and safe to use. However, you get the engine, brakes, transmission, steering, and suspension separately. Once you figure out how to properly put everything together—and manage not to screw up the coding mechanics—the car should be safe to drive. How does that sound? AES is technically not even that car, but the engine of that car: a block cipher. AES is supposed to be an “Advanced Encryption Standard,” but it fails at being one. Instead, AES is really a specification to use a block cipher called Rijndael in three defined “strength” modes (10, 12, or 14 transformation cycles). AES should have been ABCS, “Advanced Block Cipher Standard.”

Actual symmetric encryption is a cryptographic zoo of block ciphers, chaining modes, padding modes, initialization vectors, nonces, and authenticated encryption modes—and that’s just the basic stuff. Are you confident[1] in your ability to put everything together correctly? How about the rest of your team?

Never fear, however, because NIST is at your service again with a friendly SP-800-38a AES manual: 22+ pages of recommendations on how to use AES block cipher properly. That’s like asking your car dealer where the “start car” button is, and being handed a technical manual on “wiring direct-injection twin-scroll-turbo engines with dual-clutch transmissions”...in German. Doppelkupplungsgetriebe?

You do not have to be an engine mechanic in order to safely drive a car. You do not have to be a dietitian to eat healthy. You should not have to be a cryptographer to encrypt and decrypt messages with a shared secret key.

The goal of this section is to provide simple rules that lead to secure implementations and prevent you from creating yet another broken permutation to litter the charred minefield of symmetric encryption. The next chapter, “Authenticated Encryption,” is where symmetric encryption will actually get secure, but we need an encryption primitive first.

AES

AES block cipher is the only block cipher you should be comfortable using if you are not a cryptographer. That leads to a simple first rule: always use AES, and forget whatever else comes with your framework. AES has three different “strength” modes, which typically do not have a direct setting, but instead are set by using a particular AES key length. The AES algorithm is fundamentally the same, but higher “strength” modes simply do more rounds of that algorithm.

Table 5: Comparison of AES strength levels

AES-128

AES-192

AES-256

Block cipher length (same for all modes)

128 bits

128 bits

128 bits

Key length

128 bits

192 bits

256 bits

Rounds

10

12

14

AES-256 is stronger than AES-128 because it has four more rounds—not because it happens to use a longer key, since a full-entropy 128-bit key is already strong enough for all plausible purposes. The .NET Framework provides two AES implementations in AesManaged and AesCryptoServiceProvider (FIPS-approved) classes.

The managed AES class instance is about 170 times faster to create than its FIPS equivalent, with a break-even point at ~2.8k of processed data. If you always encrypt less than ~2.8k of data, managed AES might be a little faster; otherwise, use AES-CSP implementation (which is what we use most of the time for FIPS compliance). Which AES should you use: AES-128, AES-192, or AES-256? Their encryption and decryption speed is usually proportional to the number of rounds—you can estimate AES-256 to be about 40 percent slower than AES-128. However, you would only feel the full extent of algorithmic performance difference when AES-processing sufficiently long messages. On most modern systems (including modern mobile platforms) the performance difference between AES flavors is unlikely to be an issue.

Available AES implementation defaults are another important decision factor. Both Microsoft implementations default to AES-256, which means that if you want to use AES-128 or AES-192, you will constantly have to fight the defaults. It makes more sense to just accept Microsoft’s default choice. NASA lost a $125 million Mars orbiter due to metric-imperial mismatch—do not think that you can always remember to change the defaults.

If you’re still unconvinced that AES-256 is the way to go, here is a quote from LibTomCrypt (C-language crypto library):

Ideally, your application should be making at least 256-bit keys. This is not because you are to be paranoid. It is because if your PRNG has a bias of any sort, the more bits the better. For example, if you have Pr [X = 1] = ½ ± bias, where |bias| > 0 then the total amount of entropy in N bits is N * (–log2(½ + |bias|)). So if bias were 0.25 (a severe bias), a 256-bit key would have about 106 bits of entropy whereas a 128-bit key would have only 53 bits of entropy.

Here is another quote by distinguished cryptography expert Daniel J. Bernstein:

But NIST is foolishly continuing to recommend AES-128 keys, even though a 280 attack will break somebody's AES-128 key out of a batch of 248 keys.

Tip: Just use AES-256.

Key

AES implementations have a public .Key property, which can be used to get or set the byte[]-typed secret key. That public property is backed by a private container, which is initially null. The .Key getter will generate a new cryptographically strong key when the private key container is null, or will simply return an existing key when the key container is not null. This, effectively, is on-demand key generation, which speeds up AES instance construction. The generation or re-generation of the key can be explicitly triggered with a .GenerateKey() method. You can also generate the key externally with a cryptographically strong RNG and assign it to the .Key property directly. The only trick with externally generated AES keys is making sure they have the right size. If you adopt AES-256 for all your needs, external key generation becomes very simple—just get 32 bytes from a CSP-based RNG. We should also re-emphasize the obvious point that secret keys are secret. Do not include secret keys with the message. Do not hash, HMAC, XOR, salt, or deep-fry them to make any part of them non-secret, or you will fail. Keep secret keys and anything derived from them secret.

Cipher mode

Block ciphers such as AES operate on a single block, which, in the case of AES, is 16 bytes long. Cipher modes are employed to make block ciphers process arbitrary-length messages that may not fit into a single block. The only AES cipher mode you should be comfortable coding yourself is CBC. CBC is the only cipher mode supported by both managed and FIPS AES implementations in the .NET Framework, and is also the default mode. Accept that default.

Padding mode

Block ciphers such as AES operate on full blocks, yet not every message will be exactly of block-size-multiple length. The last block can often be incomplete, and various padding modes can be used to make the last incomplete block whole. Some cipher modes do not require padding, but many—CBC included—do. The important thing for you to remember is that it does not matter which padding mode you choose with CBC, because they all function as reversible padding, and are all insecure (at least the ones available in the .NET Framework). We will address this insecurity of padding modes with authenticated encryption, but we are not there yet. Since it does not matter which padding mode is used, you might as well go with the default one, which in .NET AES implementations happens to be PKCS7.

Initialization vector (IV)

Initialization vector (IV) is an additional non-secret block of entropy used by block ciphers such as AES to prevent vulnerabilities (such as ciphertext patterns) due to secret key reuse and plaintext reuse, both of which are very common and expected. We typically expect to be able to use the same secret key to encrypt more than one message, and we typically expect to be able to encrypt the same message at different points in time without revealing the “sameness” of the messages. A block cipher IV is one block long (16 bytes for AES). Different cipher modes place different requirements on the contents of an IV. Fortunately, we only need to know the IV requirements for CBC, since that’s the cipher mode we are comfortable coding ourselves.

Tip: CBC IV must be unpredictable for every execution of the encryption process.

“Unpredictable” here means “generated by CSP-based RNG.” Also note the “every execution” part: if you deviate from this CBC IV requirement in any way, you fail. If you try to be “smart” by hashing, HMACing, HKDFing, or deriving it somehow—just to avoid sending the IV along with the encrypted message—you fail. If you forget the “every execution” requirement and reuse the IV—you fail. Given such a simple and easy-to-implement CBC IV requirement, it is amazing how many implementations get it wrong. A Microsoft MVP (Most Valuable Professional) for “Developer Security” got it wrong.

AES implementations have an .IV property, which works similarly to the .Key property, and also does “on-demand” CSP-based random IV generation. The generation or re-generation of the IV can be explicitly triggered with a .GenerateIV() method. You can also generate the IV externally with a CSP-based RNG and assign it to the .IV property directly. If you take the external approach, remember that the AES IV size is always 16 bytes. Once you have performed an AES encrypt operation with a freshly generated random IV, you need to include that IV (which is not a secret) with the ciphertext to make the decryption process possible later, and you should never reuse that IV for any other encryption operation.

AES in counter mode (CTR)

While the CBC mode is the safest, misuse-resistant, built-in cipher operation mode in .NET, it is not the most secure or convenient mode for advanced usage scenarios, such as designing more complex crypto constructs that use variable-length data encryption as a building block. Counter (CTR) mode is another hugely popular mode that is technically superior to CBC mode (but not as misuse resistant), and is better suited as a data encryption component in other, more complex crypto schemes. Phillip Rogaway, a distinguished cryptography expert, said the following about CTR mode:

I am unable to think of any cryptographic design problem where, absent major legacy considerations, any of [CBC and other classic modes] would represent the mode of choice…I regard CTR as easily the best choice among the set of the confidentiality modes (meaning the set of modes aiming only for message privacy, as classically understood). It has unsurpassed performance characteristics and provable-security guarantees that are at least as good as any of the [other classic] modes with respect to classical notions of privacy. The simplicity, efficiency, and obvious correctness of CTR make it a mandatory member in any modern portfolio of [secure] schemes.

Since you should not be designing anything crypto-related, do not try to implement CTR yourself, and stick with the built-in CBC. However, a decent .NET implementation of CTR mode is available in the Inferno crypto library (not a valid reason to abandon CBC).

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.