As cryptographic research evolves and computers become faster, some cryptographic algorithms, security protocols, cryptographic key strengths, and usage are no longer deemed secure enough for software products.
To put this in perspective, the Electronic Frontier Foundation book Cracking DES claims that a specially built $1 million computer in 1993 would take, on average, about 3.5 hours to find a Data Encryption Standard (DES) key (Electronic Frontier Foundation 1998). According to Moore’s Law, $1 million in 1998 could crack a DES key in about 35 minutes. If you don’t have a spare million, spend $10,000 and you could break the key in 2.5 days. In 2006, CPU speeds are faster than in 1998, and memory is much cheaper.
This chapter outlines guidance and standards for writing new code or updating existing code covered by SDL, code which should be upgraded if advances in cryptographic research find algorithms or key sizes inadequate.
The following sections describe at a very high level the basic SDL cryptographic requirements and best practices.
Whenever possible, use an established security standard rather than creating your own solution. For example, use SSL/TLS, IPSec, or WS-Security for protecting ephemeral on-the-wire data rather than creating your own authentication, key exchange, encryption, and integrity solutions from cryptographic primitives.
Do not create your own cryptographic libraries, and certainly do not create your own cryptographic algorithms. For .NET code, you should use the class libraries defined in System .Security.Cryptography namespace (Microsoft 2006a). For C/C++ code, you should use CryptoAPI (Microsoft 2006b). For scripts (VBScript or JavaScript), you should use CAPICOM (Microsoft 2001).
For correct function and method-call usage, please refer to the references section at the end of this chapter.
Do not hard-code the cryptographic algorithm(s) used by your application within the application code. Instead, store the cryptographic primitive(s) used in a configurable store—for example, in the registry or in an XML configuration file—where they can be updated quickly by the customer in the event of a sudden and unpredictable change in cryptographic technology. Note that tampering with any data store used in this way can compromise application behavior. Therefore, to protect the cryptographic primitives, appropriate mitigations—such as a strong access control policy that allows only trusted users to manipulate the data—should be defined for the data store. It’s also worthwhile to add the cryptographic algorithms used by the payload. For example, the following could represent an encrypted and MACd data blob (RFC 2104). Note that it might look as though you are providing a lot of useful information to an attacker, but you aren’t—the strength of an encryption algorithm, such as AES, lies solely in the quality and protection of the encryption.
<?xml version="1.0" encoding="utf-8"?> <blob version="1.2"> <encryption> <alg id="AES" keySize="256" IV="LqIfly+GY0ORE3KnBjw41g==" mode="CBC" padding="PKCS7"/> <data>qAuGOVVIpQBVd ...snip... m13yt1ngkY8=</data> </encryption> <authentication> <alg id="HMACSHA56" /> <data>1y1xAI9CywYQPvau71j6eRDqgfND1yla5Hdf02xAp20=</data> </authentication> </blob>
Use strong cryptographic algorithms by default. If a weak algorithm is needed for backward compatibility with older software or to comply with an industry standard, it should be a fallback, not a default, and it should be available only on an “opt-in” basis. Silently falling back to weak cryptography is considered bad practice; users should be notified if they are falling back to a weaker algorithm. System and network administrators should have the means to control whether applications can use weak algorithms in their administrative domains. Table 20-1 in the next section defines which cryptographic algorithms are acceptable for defaults.
Table 20-1. Cryptographic Algorithm Usage Guidance
Algorithm Class | Algorithms and Key Sizes That Must Be Replaced | Algorithms and Key Sizes Okay for Existing Code | Required Algorithms and Key Sizes for New Code |
---|---|---|---|
Symmetric Block Cipher | DES, DESX, RC2, Skipjack | 3DES (112 bit or 168 bit) | AES (>= 128 bit) |
Symmetric Stream Cipher | SEAL, CYLINK_MEK, RC4 (<128 bit) | RC4 (reviewed, see below, and >= 128 bit) | None—use a block cipher |
Asymmetric Cipher | RSA or Diffie-Hellman (DH) (<1024 bit) | RSA or DH (1024-2047 bit) | RSA or DH (>=2048 bit) Elliptic Curve Cryptography (ECC) (>=256 bit) |
Hash (includes Hashed Message Authentication Codes [HMAC]) | SHA0, MD2, MD4 and MD5 | SHA1 | SHA256, SHA384 and SHA512 (also referred to as the SHA2 algorithms) |
MAC key length | <112 bit | 112–127 bits | >=128 bits |
If a project uses multiple cryptographic algorithms to maintain backward compatibility, it must not default or silently fall back to the cryptographic algorithms that are listed as “. . . Must Be Replaced” in Table 20-1.
This section focuses on how different algorithms should be approached in new and earlier code. The SDL requirements dictate that
New code uses only algorithms and key lengths from the rightmost column.
Algorithms listed in the middle column are to be used only for backward compatibility.
Algorithms and key lengths listed in the left column are not to be used in shipping products without an exception from the central security team.
Using any cryptographic algorithms that are not listed in the middle or right-hand columns requires an exception from your central security team. Be aware that the United States federal government mandates the use of specific cryptographic algorithms (NIST 2005).
For symmetric block encryption algorithms, a minimum key length of 128 bits is required for new code (KeyLength 2006). The only block encryption algorithm recommended for new code is AES. (AES-128, AES-192, and AES-256 are all acceptable.) Two-key (112-bit) or three-key (168-bit) 3DES are currently acceptable if already in use in existing code. However, transitioning to AES is highly recommended. DES, DESX, RC2, and SKIPJACK are no longer considered secure; continued use of these algorithms should be for opt-in backward compatibility only.
For symmetric stream ciphers, there is currently no recommended algorithm—you should use a block cipher, such as AES, with at least 128 bits of key material. Existing code that uses RC4 should be using a key size of at least 128 bits, and your application’s use of RC4 should be reviewed by a cryptographer. This last point is very important—there are numerous subtle errors that can arise when using stream ciphers such as RC4. Refer to the "References" section of this chapter for other material outlining some of the common errors.
Symmetric algorithms can operate in a number of modes, most of which link together the encryption operations on successive blocks of plaintext and ciphertext. The electronic code book (ECB) mode of operation should not be used without signoff from the central security team. Cipher-block-chaining (CBC) is the recommended mode of operation for block ciphers. If, for interoperability reasons, you believe that you need to use another chaining mode, you should talk to the security team.
For RSA-based asymmetric encryption and digital signatures, the minimum acceptable key length is 1024 bits, and 1024-bit signature keys should be used only for signatures with validity periods of one year or less. New code should use RSA keys of at least 2048 bits in length.
For DSA-based digital signatures, only 1024-bit keys should be used (the maximum allowed by the DSA standard) and then only for short-lived signatures (less than one year).
For key exchange and digital signatures that are based on elliptic curve cryptography (ECC), the three NIST-approved curves—P-256, P-384, and P-521—are all acceptable.
For key agreement, Diffie-Hellman is recommended, with 2048-bit keys for new code and 1024-bit keys for backward compatibility. Keys of 512 bits or fewer are not to be used at all.
For projects using asymmetric algorithms, ECC with >=256-bit keys or RSA with >=2048-bit keys is required for new code. RSA with >=1024-bit keys is permissible for backward compatibility. RSA <1024-bit keys can be used only for decrypting old data. ECC-based key exchange and digital signatures must use one of the three NIST-approved curves—P-256, P-384, and P521 are all acceptable. For key agreement, Diffie-Hellman is recommended, with >=2048-bit keys for new code, >=1024-bit keys for backward compatibility, and no keys using <1024 bits.
No new code should use the MD4 or MD5 hash algorithms because hash collisions have been demonstrated for both algorithms, which effectively “breaks” them in the eyes of the cryptographic community. Continued use of SHA-1 is permissible in existing code for backward compatibility purposes and, as described in the next Best Practices reader aid, for new code running on certain down-level platforms. The SHA-2 family of hash functions (SHA-256, SHA-384, or SHA-512) is currently the only group that is generally recommended. The SHA-2 hash functions are available in .NET code and in unmanaged Microsoft Win32 code targeting Windows Server 2003 SP1 and Windows Vista.
Note that hash function agility—the ability to switch to another hash function without updating your code—is part of the cryptographic agility requirement discussed earlier in this chapter. Absent a backward compatibility requirement, code that uses SHA-1 must migrate to SHA-2 once SHA-2 is available on the platform.
For .NET code, use of a SHA-2 hash function is required. For new native Win32 code shipping to Windows Server 2003 SP1 or Windows Vista, use of a SHA-2 hash function is required. For new native Win32 code shipping to earlier operating systems (including Windows 95, Windows 98, Microsoft Windows NT 4, and Windows 2000), use of SHA-1 is permitted. This exemption automatically expires if a service pack containing SHA-2 support ships on the platform in question. Continued use of SHA-1 is permissible for backward compatibility. All others hash functions, including MD2, MD4, and MD5, should not be used.
The most common and well-known message authentication code (MAC) function is the HMAC, which uses a hash function and secret MAC key for message authentication. It uses an underlying hash function (MD5, SHA-1, or SHA-2) and a secret key of a specified length. The strength of an HMAC relies on the strength of the underlying hash function and the length of the secret.
In this section, I will discuss issues related to cryptography, including sensitive data storage and generating random numbers.
Keys, secret data, and passwords should be protected using the Data Protection API (DPAPI). Applications must not embed private keys, encrypted or not, in code.
Security code and code using cryptographic algorithms require random numbers that exhibit unpredictability. Pseudorandom functions, such as the C runtime function rand or system functions such as GetTickCount, should therefore never be used in such code. Instead, one of the following functions or methods should be used:
CryptGenRandom (for C/C++ code)
rand_s (new C runtime library function that calls CryptGenRandom)
RNGCryptoServiceProvider (for .NET code)
GetRandom (CAPICOM for script languages)
It’s sometimes necessary to use a password or other secret data to derive cryptographic keys, typically combined with random data such as a nonce or a salt. Using a password directly as an encryption key is not allowed. Direct hashing of the password should never be used to derive session (ephemeral) keys. Direct hashing of the password should not be used to derive long-term (static) secret or private keys.
The supported way to derive cryptographic keys from passwords or other secret data is to use a well-defined and analyzed key derivation function (KDF) (RFC 2898) such as CryptDeriveKey in CAPI and PasswordDeriveBytes or Rfc2898DeriveBytes for .NET code.