186
Diffie-Hellman guarantees a shared secret will be created that is suitable to use as the key to a symmetric algorithm. Failing to provide authentication through some other means, either with
authenticated extensions to the implementation or through use of another algorithm such as DSA, leaves the protocol susceptible to man-in-the-middle attacks. Well discuss the details of this type
of attack with regard to Diffie-Hellman toward the end of this section.
8.2.1 The Basics
The low-level interface to Diffie-Hellman provided by OpenSSL consists of a structure of the type
DH
and a set of functions that operate on that structure. The
DH
structure and functions are made accessible by including the openssldh.h header file. The
DH
structure itself contains many data members that are of little or no interest to us, but four members are important, as shown in the
following abbreviated
DH
structure definition:
typedef struct dh_st {
BIGNUM p; BIGNUM g;
BIGNUM pub_key; BIGNUM priv_key;
} DH;
The
p
and
g
members, known as Diffie-Hellman parameters, are public values that must be shared between the two parties using the algorithm to create a shared secret. Because theyre public
values, no harm will come from a potential attacker discovering them, which means that they can be agreed upon beforehand or exchanged over an insecure medium. Typically, one side of the
conversation generates the parameters and shares them with the peer.
The
p
member is a prime number that is randomly generated. For temporary keys, it is typically at least 512 bits in length, whereas persistent keys should more appropriately be at least 1,024 bits.
These sizes correspond to the notion of ephemeral and static keys presented in Chapter 5
. The prime that will be used for
p
is generated such that
p-12
is also prime. Such a prime is known as a safe or strong prime . The
g
member, also known as the generator, is usually a small number greater than one. OpenSSL functions best when this number is either two or five. A value of two is
sometimes used for performance reasons, but keep in mind that faster key generation also means that an attacker can break the algorithm faster. In general, we recommend that a value of five be
used.
Using the two public parameters,
p
and
g
, each pair chooses a random large integer for the
priv_key
member. A value for the
pub_key
member is computed from the
priv_key
member and shared with the peer. It is important that only the value of the
pub_key
member be shared. The value of the
priv_key
member should never be shared. Using the values of
priv_key
and the peers
pub_key
, each peer can independently compute the shared secret. The shared secret is suitable for use as the key for a symmetric cipher. The entire
exchange between the peers can be done over an insecure medium. Even if someone captures the parameter and key exchange, the attacker will not be able to determine the shared secret.
8.2.2 Generating and Exchanging Parameters
Diffie-Hellman requires that both parties involved in the key exchange use the same parameters to generate public keys. This means the parameters either need to be agreed upon and exchanged
before the conversation begins, or the parameters must be generated and exchanged as part of the key exchange. Either way, the parameters must first be generated by one party and given to the
other, or perhaps generated by a third party and given to both. For the purposes of this discussion,
187
we will assume that the generation of the parameters will be done as part of the key exchange process, although this is often not desirable because it can take a significant amount of time to
generate the parameters.
The participants in the key agreement must first agree which party will be responsible for generating the parameters that theyll both use. In a clientserver scenario, the parameters are
usually generated by the server. Often, the server will generate the parameters when it starts up or retrieve them from a file that contains already generated parameters, and use the same parameters
for each client that connects. It is also common for both the client and server to have a copy of the parameters built into the respective applications.
OpenSSL provides the function
DH_generate_parameters
, which will create a new DH object that is initialized with fresh values for
p
and
g
. The generation of the parameters generates a value only for
p
. The value of
g
is specified by the caller and is not chosen randomly by OpenSSL. The value of
g
should be a small number greater than one, usually either two or five.
DH DH_generate_parametersint prime_len, int generator, void callbackint, int, void , void
cb_arg;
prime_len The size of the prime to be generated, specified in terms of bits.
generator The value to be used for
g
. In general, either
DH_GENERATOR_2
or
DH_GENERATOR_5
should be used for this argument. callback
A pointer to a function that will be called during the prime generation process to report the status of the prime generation. The callback is the same as the callback used by
BN_generate_prime
, which we discussed in Chapter 4
. In fact,
DH_generate_parameters
uses
BN_generate_prime
, and it is
BN_generate_prime
that actually makes the calls to the callback function. This argument may be specified as
NULL
if no callbacks are desired. cb_arg
A pointer to application-specific data. OpenSSL does not use this value for anything itself. It is used only when passed as an argument to the specified callback function.
Using the generation function alone can be dangerous. While the generation function does have validity checks for the prime that it generates, it could generate a prime that is not suitable for use
with the algorithm. For this reason, the function
DH_check
should always be used to ensure that the generated prime is suitable.
int DH_checkDH dh, int codes;
dh The DH object containing the parameters we wish to check.
codes
188
An integer that will be treated as a bit mask by
DH_check
and will contain the results of the check when the function returns successfully.
If the function encounters an error unrelated to the validity of the generated prime, the return will be zero; otherwise, it will be nonzero. When the function returns successfully, the
codes
argument will contain a bit mask that indicates whether the parameters are suitable for use or not. If none of the bits is set, the parameters should be considered suitable for use. If any of the
following bits are set, the parameters may not be suitable for use. In most cases, the parameters should be thrown away, and new ones should be generated.
DH_CHECK_P_NOT_PRIME If this bit is set, it indicates that the generated prime is not actually a prime number.
Ordinarily, this bit should never be set when the parameters are generated using
DH_generate_parameters
, but it could very well be set when checking parameters retrieved from disk or from a peer.
DH_CHECK_P_NOT_SAFE_PRIME If this bit is set, it indicates that the generated prime is not safe. That is,
p-12
is not also a prime number. As with
DH_CHECK_P_NOT_PRIME
, this bit should never be set when the parameters were generated using
DH_generate_parameters
, but it could very well be set when checking parameters retrieved from disk or from a peer.
DH_NOT_SUITABLE_GENERATOR If this bit is set, it indicates that the generated prime and the generator are not suitable for
use together. The parameters dont necessarily need to be thrown away and regenerated if this bit is set. Instead, the generator could be changed and the check retried.
DH_UNABLE_TO_CHECK_GENERATOR If this bit is set, a nonstandard generator is being used, so the
DH_check
function is unable to check to see that the prime and the generator are suitable for use. If you know
that youve set a nonstandard generator intentionally, its up to you to decide whether it is safe to ignore this bit being set or not.
Once the parameters have been generated, they can be transmitted to the peer. The details of how the data is sent depend on the medium that is being used for the exchange. To transmit the
parameters over a TCP connection, the BIGNUM functions
BN_bn2bin
and
BN_bin2bn
are obvious candidates.
The party that generates the parameters calls
DH_generate_parameters
to obtain a
DH
object. The party that is receiving the parameters must also obtain a
DH
object. This is easily done by calling the function
DH_new
, which will allocate and initialize a new
DH
object. The parameters that are received from the peer can then be directly assigned to the
DH
objects
p
and
g
data members, using the appropriate BIGNUM functions.
When were done with a
DH
object, we must be sure to destroy it by calling the function
DH_free
, and passing the pointer returned by either
DH_generate_parameters
or
DH_new
as the only argument.
8.2.3 Computing Shared Secrets