161
break; }
} XOR the key stream with the first buffer, placing the results
in the second buffer.
for i = 0; i len; i++ ct[i] = pt[i] encr_ctrs[i];
return 1; Success. }
As we discussed, the above example requires the state of the counter to be kept externally. Another option is to make a
COUNTER_CTX
data type that could hold a pointer to the underlying cipher context and the current state of the counter. However, this less-abstract API makes it easier
to use in a situation in which the counter may need to be reset explicitly after desynchronization, such as when dealing with UDP traffic.
6.3 General Recommendations
So far, we have looked at how to use the EVP API to perform encryption and decryption. While weve examined some basic examples, we havent looked at real-world examples. The primary
reason is that we dont recommend using symmetric key encryption without a MAC; in cases where an attacker has read access to data, you should be worried about her also gaining write
access. Therefore, we give real-world examples of using encryption along with MACs in
Chapter 7
. When you do use MACs, use them with independent keys that is, do not MAC with your
encryption keys and use them to validate all of the data, including anything sent in the clear. In particular, when using counter mode, make sure to include the counter value in the data that you
include in the MAC calculation.
Extending this recommendation, whenever you design protocols based on encryption, avoid any communication in plaintext at all, and MAC anything that does need to be plaintext. In particular,
if you do plaintext protocol negotiation before a key exchange, you should MAC the payloads of each message in the negotiation, so that after a key is agreed upon, both sides can validate the
negotiation. For example, lets say that a client connects to the server and immediately asks to speak Version 2 of protocol X, and receives a response saying the server speaks only the insecure
Version 1. If it turns out that a man in the middle told the server the client wanted Version 1, and fakes the response from the server, then neither the client nor the server would notice, and would
wind up speaking an insecure version of the protocol.
Another recommendation is to design your protocol to be fault-tolerant. In particular, when using MACs to validate messages, be prepared to perform reasonable error handling if the data doesnt
authenticate on the receiving end. If your protocol fails in such a situation, denial of service attacks will be quite easy.
Finally, be sure to protect yourself against dictionary and capture-replay type attacks. One thing you can do is add sequence numbers to the beginning of each message. Its also a good idea to
place unique information per-user or per-connection near the beginning of each message.
162
Chapter 7. Hashes and MACs
In the previous chapter, we looked at the most fundamental part of OpenSSLs cryptography library, symmetric ciphers. In this chapter, we look at the API for cryptographic hashing
algorithms, also commonly called message digest algorithms or cryptographic one-way hash functions. Additionally, we will examine OpenSSLs interface to message authentication codes
MACs, also known as keyed hashes.
7.1 Overview of Hashes and MACs
We introduced the basic concepts behind cryptographic hashes and MACs in Chapter 1
. Here, we describe the fundamental properties of these cryptographic primitives that you should understand
before integrating them into your applications. As mentioned in Chapter 6
, we provide only the minimum background information that you need to understand as a developer. If you need more
background, or would like to see under the hood of any of the algorithms we discuss, refer to a general-purpose cryptography reference, such as Bruce Schneiers Applied Cryptography.
Cryptographic one-way hashes take arbitrary binary data as an input and produce a fixed-size binary string as an output, called the hash value or the message digest. Passing the same message
through a single hash function always yields the same result. There are several important properties exhibited by cryptographic message digests. First, the digest value should contain no
information that could be used to determine the original input. For that to be true, a one-bit change in the input data should change many bits in the digest value on average, half. Second, it should
be extremely difficult to construct a second message that yields the same resulting hash value. Third, it should also be difficult to find any two messages that yield the same hash value.
The most conservative characterization of the security afforded by a given hash function is measured by how hard it is to find two arbitrary messages that yield the same hash value.
Generally, the security of a well-respected hash function that has a digest size of n bits should be about as secure as a well-respected symmetric cipher with half as many bits. For example, SHA1,
which has a 160-bit digest size, is about as resistant to attack as RC5 with 80-bit keys. Some uses of these algorithms give security equal to their bit length thats just a good, conservative metric.
People frequently use cryptographic hash functions that they believe provide security, but that dont really provide very much. For example, it is common to release software along with an MD5
digest of the software package MD5 is a common cryptographic hash function. The intention is to use the digest as a checksum. The person downloading software should also obtain the MD5
digest, and then calculate the digest himself on the downloaded software. If the two digests match, it would indicate that the downloaded software is unaltered.
Unfortunately, there are easy ways to attack this scheme. Suppose an attacker has maliciously modified a copy of the distribution of software package X, resulting in package Y. If the attacker
can break onto the server and replace X with Y, then certainly, the checksum MD5X is also easily replaceable with the checksum MD5Y. When a user validates the downloaded checksum,
he will be none the wiser. Even without access to the actual server, attackers could replace X with Y and MD5X with MD5Y as they traverse the network.
The fundamental problem is that nothing in this scenario is secret. A much better solution for this kind of scenario is a digital signature, which anyone can verify, but only someone with the correct
private key can generate see Chapter 8
.