cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ctx_dh.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Diffie-Hellman Key Exchange Routines *
4 * Copyright Peter Gutmann 1995-2005 *
5 * *
6 ****************************************************************************/
7 
8 #define PKC_CONTEXT /* Indicate that we're working with PKC contexts */
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "context.h"
12 #else
13  #include "crypt.h"
14  #include "context/context.h"
15 #endif /* Compiler-specific includes */
16 
17 /* The DH key exchange process is somewhat complex because there are two
18  phases involved for both sides, an "export" and an "import" phase, and
19  they have to be performed in the correct order. The sequence of
20  operations is:
21 
22  A.load: set p, g from fixed or external values
23  x(A) = rand, x s.t. 0 < x < q-1
24 
25  A.export y(A) = g^x(A) mod p error if y != 0 at start
26  output = y(A)
27 
28  B.load read p, g / set p, g from external values
29  x(B) = rand, x s.t. 0 < x < q-1
30 
31  B.import y(A) = input
32  z = y(A)^x(B) mod p
33 
34  B.export y(B) = g^x(B) mod p error if y != 0 at start
35  output = y(B)
36 
37  A.import y(B) = input
38  z = y(B)^x(A) mod p
39 
40  Note that we have to set x when we load p and g because otherwise we'd
41  have to set x(A) on export and x(B) on import, which is tricky since the
42  DH code doesn't know whether it's working with A or B */
43 
44 #ifdef USE_DH
45 
46 /****************************************************************************
47 * *
48 * Algorithm Self-test *
49 * *
50 ****************************************************************************/
51 
52 /* Perform a pairwise consistency test on a public/private key pair */
53 
55 static BOOLEAN pairwiseConsistencyTest( INOUT CONTEXT_INFO *contextInfoPtr )
56  {
57  CONTEXT_INFO checkContextInfo;
58  PKC_INFO *sourcePkcInfo = contextInfoPtr->ctxPKC;
59  PKC_INFO contextData, *pkcInfo = &contextData;
60  KEYAGREE_PARAMS keyAgreeParams1, keyAgreeParams2;
62  int bnStatus = BN_STATUS, status;
63 
64  assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
65 
66  /* The DH pairwise check is a bit more complex than the one for the
67  other algorithms because there's no matched public/private key pair,
68  so we have to load a second DH key to use for key agreement with
69  the first one */
70  status = staticInitContext( &checkContextInfo, CONTEXT_PKC,
71  getDHCapability(), &contextData,
72  sizeof( PKC_INFO ), NULL );
73  if( cryptStatusError( status ) )
74  return( FALSE );
75  CKPTR( BN_copy( &pkcInfo->dlpParam_p, &sourcePkcInfo->dlpParam_p ) );
76  CKPTR( BN_copy( &pkcInfo->dlpParam_g, &sourcePkcInfo->dlpParam_g ) );
77  CKPTR( BN_copy( &pkcInfo->dlpParam_q, &sourcePkcInfo->dlpParam_q ) );
78  CKPTR( BN_copy( &pkcInfo->dlpParam_y, &sourcePkcInfo->dlpParam_y ) );
79  CKPTR( BN_copy( &pkcInfo->dlpParam_x, &sourcePkcInfo->dlpParam_x ) );
80  if( bnStatusError( bnStatus ) )
81  {
82  staticDestroyContext( &checkContextInfo );
83  return( getBnStatus( bnStatus ) );
84  }
85 
86  /* Perform the pairwise test using the check key */
87  capabilityInfoPtr = checkContextInfo.capabilityInfo;
88  memset( &keyAgreeParams1, 0, sizeof( KEYAGREE_PARAMS ) );
89  memset( &keyAgreeParams2, 0, sizeof( KEYAGREE_PARAMS ) );
90  status = capabilityInfoPtr->initKeyFunction( &checkContextInfo, NULL, 0 );
91  if( cryptStatusOK( status ) )
92  status = capabilityInfoPtr->encryptFunction( contextInfoPtr,
93  ( BYTE * ) &keyAgreeParams1, sizeof( KEYAGREE_PARAMS ) );
94  if( cryptStatusOK( status ) )
95  status = capabilityInfoPtr->encryptFunction( &checkContextInfo,
96  ( BYTE * ) &keyAgreeParams2, sizeof( KEYAGREE_PARAMS ) );
97  if( cryptStatusOK( status ) )
98  status = capabilityInfoPtr->decryptFunction( contextInfoPtr,
99  ( BYTE * ) &keyAgreeParams2, sizeof( KEYAGREE_PARAMS ) );
100  if( cryptStatusOK( status ) )
101  status = capabilityInfoPtr->decryptFunction( &checkContextInfo,
102  ( BYTE * ) &keyAgreeParams1, sizeof( KEYAGREE_PARAMS ) );
103  if( cryptStatusError( status ) || \
104  keyAgreeParams1.wrappedKeyLen != keyAgreeParams2.wrappedKeyLen || \
105  memcmp( keyAgreeParams1.wrappedKey, keyAgreeParams2.wrappedKey,
106  keyAgreeParams1.wrappedKeyLen ) )
107  status = CRYPT_ERROR_FAILED;
108 
109  /* Clean up */
110  staticDestroyContext( &checkContextInfo );
111 
112  return( cryptStatusOK( status ) ? TRUE : FALSE );
113  }
114 
115 #ifndef CONFIG_NO_SELFTEST
116 
117 /* Test the Diffie-Hellman implementation using a sample key. Because a lot
118  of the high-level encryption routines don't exist yet, we cheat a bit and
119  set up a dummy encryption context with just enough information for the
120  following code to work */
121 
122 typedef struct {
123  const int pLen; const BYTE p[ 128 ];
124  const int qLen; const BYTE q[ 20 ];
125  const int gLen; const BYTE g[ 128 ];
126  const int xLen; const BYTE x[ 20 ];
127  const int yLen; const BYTE y[ 128 ];
128  } DLP_KEY;
129 
130 static const DLP_KEY FAR_BSS dlpTestKey = {
131  /* p */
132  128,
133  { 0x04, 0x4C, 0xDD, 0x5D, 0xB6, 0xED, 0x23, 0xAE,
134  0xB2, 0xA7, 0x59, 0xE6, 0xF8, 0x3D, 0xA6, 0x27,
135  0x85, 0xF2, 0xFE, 0xE2, 0xE8, 0xF3, 0xDA, 0xA3,
136  0x7B, 0xD6, 0x48, 0xD4, 0x44, 0xCA, 0x6E, 0x10,
137  0x97, 0x6C, 0x1D, 0x6C, 0x39, 0xA7, 0x0C, 0x88,
138  0x8E, 0x1F, 0xDD, 0xF7, 0x59, 0x69, 0xDA, 0x36,
139  0xDD, 0xB8, 0x3E, 0x1A, 0xD2, 0x91, 0x3E, 0x30,
140  0xB1, 0xB5, 0xC2, 0xBC, 0xA9, 0xA3, 0xA5, 0xDE,
141  0xC7, 0xCF, 0x51, 0x2C, 0x1B, 0x89, 0xD0, 0x71,
142  0xE3, 0x71, 0xBB, 0x50, 0x86, 0x26, 0x32, 0x9F,
143  0xF5, 0x4A, 0x9C, 0xB1, 0x78, 0x7B, 0x47, 0x1F,
144  0x19, 0xC7, 0x26, 0x22, 0x15, 0x62, 0x71, 0xAB,
145  0xD7, 0x25, 0xA5, 0xE4, 0x68, 0x71, 0x93, 0x5D,
146  0x1F, 0x29, 0x01, 0x05, 0x9C, 0x57, 0x3A, 0x09,
147  0xB0, 0xB8, 0xE4, 0xD2, 0x37, 0x90, 0x36, 0x2F,
148  0xBF, 0x1E, 0x74, 0xB4, 0x6B, 0xE4, 0x66, 0x07 },
149 
150  /* q */
151  20,
152  { 0xFD, 0xD9, 0xC8, 0x5F, 0x73, 0x62, 0xC9, 0x79,
153  0xEF, 0xD5, 0x09, 0x07, 0x02, 0xE7, 0xF2, 0x90,
154  0x97, 0x13, 0x26, 0x1D },
155 
156  /* g */
157  128,
158  { 0x02, 0x4E, 0xDD, 0x0D, 0x7F, 0x4D, 0xB1, 0x42,
159  0x01, 0x50, 0xE7, 0x9A, 0x65, 0x73, 0x8B, 0x31,
160  0x24, 0x6B, 0xC6, 0x74, 0xA7, 0x68, 0x26, 0x11,
161  0x06, 0x3C, 0x96, 0xA9, 0xA6, 0x23, 0x12, 0x79,
162  0xC4, 0xEE, 0x21, 0x88, 0xDD, 0xE3, 0xF0, 0x37,
163  0xCE, 0x3E, 0x54, 0x53, 0x57, 0x03, 0x30, 0xE4,
164  0xD3, 0xAB, 0x39, 0x4E, 0x39, 0xDC, 0xA2, 0x88,
165  0x82, 0xF6, 0xE8, 0xBA, 0xAC, 0xF5, 0x7D, 0x2F,
166  0x23, 0x9A, 0x09, 0x94, 0xB2, 0x89, 0xA2, 0xC9,
167  0x7C, 0xBE, 0x4D, 0x48, 0x0E, 0x59, 0x51, 0xB8,
168  0x7D, 0x99, 0x88, 0x79, 0xA8, 0x13, 0x0E, 0x12,
169  0x56, 0x9D, 0x4B, 0x2E, 0xE0, 0xE1, 0x37, 0x78,
170  0x6F, 0xCC, 0x4D, 0x97, 0xA9, 0x02, 0x0E, 0xD2,
171  0x43, 0x83, 0xEC, 0x4F, 0xC2, 0x70, 0xEF, 0x16,
172  0xDE, 0xBF, 0xBA, 0xD1, 0x6C, 0x8A, 0x36, 0xEE,
173  0x42, 0x41, 0xE9, 0xE7, 0x66, 0xAE, 0x46, 0x3B },
174 
175  /* x */
176  20,
177  { 0xD9, 0x41, 0x29, 0xF7, 0x40, 0x32, 0x09, 0x71,
178  0xB8, 0xE2, 0xB8, 0xCB, 0x74, 0x46, 0x0B, 0xD4,
179  0xF2, 0xAB, 0x54, 0xA1 },
180 
181  /* y */
182  128,
183  { 0x01, 0x7E, 0x16, 0x5B, 0x65, 0x51, 0x0A, 0xDA,
184  0x82, 0x1A, 0xD9, 0xF4, 0x1E, 0x66, 0x6D, 0x7D,
185  0x23, 0xA6, 0x28, 0x2F, 0xE6, 0xC2, 0x03, 0x8E,
186  0x8C, 0xAB, 0xC2, 0x08, 0x87, 0xC9, 0xE8, 0x51,
187  0x0A, 0x37, 0x1E, 0xD4, 0x41, 0x7F, 0xA2, 0xC5,
188  0x48, 0x26, 0xB7, 0xF6, 0xC2, 0x6F, 0xB2, 0xF8,
189  0xF9, 0x43, 0x43, 0xF9, 0xDA, 0xAB, 0xA2, 0x59,
190  0x27, 0xBA, 0xC9, 0x1C, 0x8C, 0xAB, 0xC4, 0x90,
191  0x27, 0xE1, 0x10, 0x39, 0x6F, 0xD2, 0xCD, 0x7C,
192  0xD1, 0x0B, 0xFA, 0x28, 0xD2, 0x7A, 0x7B, 0x52,
193  0x8A, 0xA0, 0x5A, 0x0F, 0x10, 0xF7, 0xBA, 0xFD,
194  0x33, 0x0C, 0x3C, 0xCE, 0xE5, 0xF2, 0xF6, 0x92,
195  0xED, 0x04, 0xBF, 0xD3, 0xF8, 0x3D, 0x39, 0xCC,
196  0xAA, 0xCC, 0x0B, 0xB2, 0x6B, 0xD8, 0xB2, 0x8A,
197  0x5C, 0xCE, 0xDA, 0xF9, 0xE1, 0xA7, 0x23, 0x50,
198  0xDC, 0xCE, 0xA4, 0xD5, 0xA5, 0x4F, 0x08, 0x0F }
199  };
200 
201 CHECK_RETVAL \
202 static int selfTest( void )
203  {
204  CONTEXT_INFO contextInfo;
205  PKC_INFO contextData, *pkcInfo = &contextData;
206  int status;
207 
208  /* Initialise the key components */
209  status = staticInitContext( &contextInfo, CONTEXT_PKC,
210  getDHCapability(), &contextData,
211  sizeof( PKC_INFO ), NULL );
212  if( cryptStatusError( status ) )
213  return( CRYPT_ERROR_FAILED );
214  status = importBignum( &pkcInfo->dlpParam_p, dlpTestKey.p,
215  dlpTestKey.pLen, DLPPARAM_MIN_P,
217  if( cryptStatusOK( status ) )
218  status = importBignum( &pkcInfo->dlpParam_g, dlpTestKey.g,
219  dlpTestKey.gLen, DLPPARAM_MIN_G,
220  DLPPARAM_MAX_G, &pkcInfo->dlpParam_p,
222  if( cryptStatusOK( status ) )
223  status = importBignum( &pkcInfo->dlpParam_q, dlpTestKey.q,
224  dlpTestKey.qLen, DLPPARAM_MIN_Q,
225  DLPPARAM_MAX_Q, &pkcInfo->dlpParam_p,
227  if( cryptStatusOK( status ) )
228  status = importBignum( &pkcInfo->dlpParam_y, dlpTestKey.y,
229  dlpTestKey.yLen, DLPPARAM_MIN_Y,
230  DLPPARAM_MAX_Y, &pkcInfo->dlpParam_p,
232  if( cryptStatusOK( status ) )
233  status = importBignum( &pkcInfo->dlpParam_x, dlpTestKey.x,
234  dlpTestKey.xLen, DLPPARAM_MIN_X,
235  DLPPARAM_MAX_X, &pkcInfo->dlpParam_p,
237  if( cryptStatusError( status ) )
238  {
239  staticDestroyContext( &contextInfo );
240  retIntError();
241  }
242 
243  /* Perform the test key exchange on a block of data */
244  status = contextInfo.capabilityInfo->initKeyFunction( &contextInfo, NULL, 0 );
245  if( cryptStatusOK( status ) && \
246  !pairwiseConsistencyTest( &contextInfo ) )
247  status = CRYPT_ERROR_FAILED;
248 
249  /* Clean up */
250  staticDestroyContext( &contextInfo );
251 
252  return( status );
253  }
254 #else
255  #define selfTest NULL
256 #endif /* !CONFIG_NO_SELFTEST */
257 
258 /****************************************************************************
259 * *
260 * Diffie-Hellman Key Exchange Routines *
261 * *
262 ****************************************************************************/
263 
264 /* Perform phase 1 of Diffie-Hellman ("export"). We have to append the
265  distinguisher 'Fn' to the name since some systems already have 'encrypt'
266  and 'decrypt' in their standard headers */
267 
268 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
269 static int encryptFn( INOUT CONTEXT_INFO *contextInfoPtr,
270  INOUT_BUFFER_FIXED( noBytes ) BYTE *buffer,
271  IN_LENGTH_FIXED( sizeof( KEYAGREE_PARAMS ) ) int noBytes )
272  {
273  PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
274  KEYAGREE_PARAMS *keyAgreeParams = ( KEYAGREE_PARAMS * ) buffer;
275  int status;
276 
277  assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
278  assert( isWritePtr( keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ) );
279 
280  REQUIRES( noBytes == sizeof( KEYAGREE_PARAMS ) );
281  REQUIRES( !BN_is_zero( &pkcInfo->dlpParam_y ) );
282 
283  /* y is generated either at keygen time for static DH or as a side-effect
284  of the implicit generation of the x value for ephemeral DH, so all we
285  have to do is copy it to the output */
286  status = exportBignum( keyAgreeParams->publicValue, CRYPT_MAX_PKCSIZE,
287  &keyAgreeParams->publicValueLen,
288  &pkcInfo->dlpParam_y );
289  if( cryptStatusError( status ) )
290  return( status );
291 
292  /* Perform side-channel attack checks if necessary */
293  if( ( contextInfoPtr->flags & CONTEXT_FLAG_SIDECHANNELPROTECTION ) && \
294  cryptStatusError( calculateBignumChecksum( pkcInfo,
295  CRYPT_ALGO_DH ) ) )
296  {
297  return( CRYPT_ERROR_FAILED );
298  }
299  return( CRYPT_OK );
300  }
301 
302 /* Perform phase 2 of Diffie-Hellman ("import") */
303 
304 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
305 static int decryptFn( INOUT CONTEXT_INFO *contextInfoPtr,
306  INOUT_BUFFER_FIXED( noBytes ) BYTE *buffer,
307  IN_LENGTH_FIXED( sizeof( KEYAGREE_PARAMS ) ) int noBytes )
308  {
309  KEYAGREE_PARAMS *keyAgreeParams = ( KEYAGREE_PARAMS * ) buffer;
310  PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
311  BIGNUM *z = &pkcInfo->tmp1;
312  int bnStatus = BN_STATUS, status;
313 
314  assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
315  assert( isWritePtr( keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ) );
316  assert( isReadPtr( keyAgreeParams->publicValue,
317  keyAgreeParams->publicValueLen ) );
318 
319  REQUIRES( noBytes == sizeof( KEYAGREE_PARAMS ) );
320  REQUIRES( keyAgreeParams->publicValueLen >= MIN_PKCSIZE && \
321  keyAgreeParams->publicValueLen < MAX_INTLENGTH_SHORT );
322 
323  /* The other party's y value will be stored with the key agreement info
324  rather than having been read in when we read the DH public key */
325  status = importBignum( &pkcInfo->dhParam_yPrime,
326  keyAgreeParams->publicValue,
327  keyAgreeParams->publicValueLen,
329  &pkcInfo->dlpParam_p, KEYSIZE_CHECK_PKC );
330  if( cryptStatusError( status ) )
331  return( status );
332 
333  /* Export z = y^x mod p. We need to use separate y and z values because
334  the bignum code can't handle modexp with the first two parameters the
335  same */
336  CK( BN_mod_exp_mont( z, &pkcInfo->dhParam_yPrime, &pkcInfo->dlpParam_x,
337  &pkcInfo->dlpParam_p, pkcInfo->bnCTX,
338  &pkcInfo->dlpParam_mont_p ) );
339  if( bnStatusError( bnStatus ) )
340  return( getBnStatus( bnStatus ) );
341  status = exportBignum( keyAgreeParams->wrappedKey, CRYPT_MAX_PKCSIZE,
342  &keyAgreeParams->wrappedKeyLen, z );
343  if( cryptStatusError( status ) )
344  return( status );
345 
346  /* Perform side-channel attack checks if necessary */
347  if( ( contextInfoPtr->flags & CONTEXT_FLAG_SIDECHANNELPROTECTION ) && \
348  cryptStatusError( calculateBignumChecksum( pkcInfo,
349  CRYPT_ALGO_DH ) ) )
350  {
351  return( CRYPT_ERROR_FAILED );
352  }
353  return( CRYPT_OK );
354  }
355 
356 /****************************************************************************
357 * *
358 * Key Management *
359 * *
360 ****************************************************************************/
361 
362 /* Load key components into an encryption context */
363 
365 static int initKey( INOUT CONTEXT_INFO *contextInfoPtr,
366  IN_BUFFER_OPT( keyLength ) const void *key,
367  IN_LENGTH_SHORT_OPT const int keyLength )
368  {
369  PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
370 
371  assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
372  assert( ( key == NULL && keyLength == 0 ) || \
373  ( isReadPtr( key, keyLength ) && \
374  keyLength == sizeof( CRYPT_PKCINFO_DLP ) ) );
375 
376  REQUIRES( ( key == NULL && keyLength == 0 ) || \
377  ( key != NULL && keyLength == sizeof( CRYPT_PKCINFO_DLP ) ) );
378 
379 #ifndef USE_FIPS140
380  /* Load the key component from the external representation into the
381  internal bignums unless we're doing an internal load */
382  if( key != NULL )
383  {
384  const CRYPT_PKCINFO_DLP *dhKey = ( CRYPT_PKCINFO_DLP * ) key;
385  int status;
386 
387  contextInfoPtr->flags |= ( dhKey->isPublicKey ) ? \
389  status = importBignum( &pkcInfo->dlpParam_p, dhKey->p,
390  bitsToBytes( dhKey->pLen ),
393  if( cryptStatusOK( status ) )
394  status = importBignum( &pkcInfo->dlpParam_g, dhKey->g,
395  bitsToBytes( dhKey->gLen ),
397  &pkcInfo->dlpParam_p,
399  if( cryptStatusOK( status ) )
400  status = importBignum( &pkcInfo->dlpParam_q, dhKey->q,
401  bitsToBytes( dhKey->qLen ),
403  &pkcInfo->dlpParam_p,
405  if( cryptStatusOK( status ) )
406  status = importBignum( &pkcInfo->dlpParam_y, dhKey->y,
407  bitsToBytes( dhKey->yLen ),
409  &pkcInfo->dlpParam_p,
411  if( cryptStatusOK( status ) && !dhKey->isPublicKey )
412  status = importBignum( &pkcInfo->dlpParam_x, dhKey->x,
413  bitsToBytes( dhKey->xLen ),
415  &pkcInfo->dlpParam_p,
417  contextInfoPtr->flags |= CONTEXT_FLAG_PBO;
418  if( cryptStatusError( status ) )
419  return( status );
420  }
421 #endif /* USE_FIPS140 */
422 
423  /* Complete the key checking and setup. DH keys may follow PKCS #3
424  rather than X9.42 which means that we can't do extended checking
425  using q, so if q is zero we denote it as a PKCS #3 key. This is
426  only permitted for DH keys and PGP Elgamal keys, other key types will
427  fail the check if q = 0 */
428  return( initCheckDLPkey( contextInfoPtr, TRUE,
429  BN_is_zero( &pkcInfo->dlpParam_q ) ? \
430  TRUE : FALSE ) );
431  }
432 
433 /* Generate a key into an encryption context */
434 
436 static int generateKey( INOUT CONTEXT_INFO *contextInfoPtr,
438  const int keySizeBits )
439  {
440  int status;
441 
442  assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
443 
444  REQUIRES( keySizeBits >= bytesToBits( MIN_PKCSIZE ) && \
445  keySizeBits <= bytesToBits( CRYPT_MAX_PKCSIZE ) );
446 
447  status = generateDLPkey( contextInfoPtr, keySizeBits );
448  if( cryptStatusOK( status ) &&
449 #ifndef USE_FIPS140
450  ( contextInfoPtr->flags & CONTEXT_FLAG_SIDECHANNELPROTECTION ) &&
451 #endif /* USE_FIPS140 */
452  !pairwiseConsistencyTest( contextInfoPtr ) )
453  {
454  DEBUG_DIAG(( "Consistency check of freshly-generated DH key "
455  "failed" ));
456  assert( DEBUG_WARN );
457  status = CRYPT_ERROR_FAILED;
458  }
459  return( status );
460  }
461 
462 /****************************************************************************
463 * *
464 * Capability Access Routines *
465 * *
466 ****************************************************************************/
467 
468 static const CAPABILITY_INFO FAR_BSS capabilityInfo = {
469  CRYPT_ALGO_DH, bitsToBytes( 0 ), "Diffie-Hellman", 14,
471  selfTest, getDefaultInfo, NULL, NULL, initKey, generateKey, encryptFn, decryptFn
472  };
473 
474 const CAPABILITY_INFO *getDHCapability( void )
475  {
476  return( &capabilityInfo );
477  }
478 
479 #endif /* USE_DH */