# HG changeset patch
# Parent  6dbcd9b97c79050cf36a9eb2adddbf3439b8b870
Diffie-Hellman key exchange selftest for FIPS 140-2
bsc#921783

diff --git a/lib/freebl/blapit.h b/lib/freebl/blapit.h
--- a/lib/freebl/blapit.h
+++ b/lib/freebl/blapit.h
@@ -8,16 +8,20 @@
 #ifndef _BLAPIT_H_
 #define _BLAPIT_H_
 
 #include "seccomon.h"
 #include "prlink.h"
 #include "plarena.h"
 #include "ecl-exp.h"
 
+/* special pointer value used as an indicator - ones' complement of zero. On
+ * Linux this should be a kernel space address which shouldn't be used to pass
+ * around user space data */
+#define ZERO_OC_POINTER		((void *)(~0))
 
 /* RC2 operation modes */
 #define NSS_RC2			0
 #define NSS_RC2_CBC		1
 
 /* RC5 operation modes */
 #define NSS_RC5                 0
 #define NSS_RC5_CBC             1
diff --git a/lib/freebl/dh.c b/lib/freebl/dh.c
--- a/lib/freebl/dh.c
+++ b/lib/freebl/dh.c
@@ -135,73 +135,100 @@ cleanup:
 SECStatus 
 DH_NewKey(DHParams *params, DHPrivateKey **privKey)
 {
     PLArenaPool *arena;
     DHPrivateKey *key;
     mp_int g, xa, p, Ya;
     mp_err   err = MP_OKAY;
     SECStatus rv = SECSuccess;
+    PRBool havePrivateValue = PR_FALSE;
+
     if (!params || !privKey) {
 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 	return SECFailure;
     }
+    /* FIPS selftest has to be a known answer one, namely: generate public
+     * value based on parameters (g, p) and private key and compare it with
+     * precomputed value. Thus a special path is needed that will allow passing
+     * a particular private key value to this function. 
+     * Since params is used as an indicator, base (g) and prime (p) already
+     * need to be in privKey alongside privateValue and an initialised arena
+     * from which the public key is going to be allocated.
+     * Note: g, p and private secret don't necessarily need to be allocated
+     * from that arena, as these are left touched on error. */
+    if ((DHParams *)(ZERO_OC_POINTER) == params) {
+	key = *privKey;
+	arena = key->arena;
+	havePrivateValue = PR_TRUE;
+	goto have_privKey;
+    }
     arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE);
     if (!arena) {
 	PORT_SetError(SEC_ERROR_NO_MEMORY);
 	return SECFailure;
     }
     key = (DHPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DHPrivateKey));
     if (!key) {
 	PORT_SetError(SEC_ERROR_NO_MEMORY);
 	PORT_FreeArena(arena, PR_TRUE);
 	return SECFailure;
     }
     key->arena = arena;
+have_privKey:
     MP_DIGITS(&g)  = 0;
     MP_DIGITS(&xa) = 0;
     MP_DIGITS(&p)  = 0;
     MP_DIGITS(&Ya) = 0;
     CHECK_MPI_OK( mp_init(&g)  );
     CHECK_MPI_OK( mp_init(&xa) );
     CHECK_MPI_OK( mp_init(&p)  );
     CHECK_MPI_OK( mp_init(&Ya) );
     /* Set private key's p */
-    CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->prime, &params->prime) );
+    if (!havePrivateValue)
+	CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->prime, &params->prime) );
     SECITEM_TO_MPINT(key->prime, &p);
     /* Set private key's g */
-    CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->base, &params->base) );
+    if (!havePrivateValue)
+	CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->base, &params->base) );
     SECITEM_TO_MPINT(key->base, &g);
     /* Generate private key xa */
-    SECITEM_AllocItem(arena, &key->privateValue,
-                      dh_GetSecretKeyLen(params->prime.len));
-    CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(key->privateValue.data, 
-                                  key->privateValue.len));
+    if (!havePrivateValue) {
+	SECITEM_AllocItem(arena, &key->privateValue,
+			dh_GetSecretKeyLen(params->prime.len));
+	CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(key->privateValue.data, 
+				    key->privateValue.len));
+    }
     SECITEM_TO_MPINT( key->privateValue, &xa );
     /* xa < p */
     CHECK_MPI_OK( mp_mod(&xa, &p, &xa) );
     /* Compute public key Ya = g ** xa mod p */
     CHECK_MPI_OK( mp_exptmod(&g, &xa, &p, &Ya) );
     MPINT_TO_SECITEM(&Ya, &key->publicValue, key->arena);
-    *privKey = key;
+    if (!havePrivateValue)
+	*privKey = key;
 cleanup:
     mp_clear(&g);
     mp_clear(&xa);
     mp_clear(&p);
     mp_clear(&Ya);
     if (err) {
 	MP_TO_SEC_ERROR(err);
 	rv = SECFailure;
     }
     if (rv) {
-	SECITEM_ZfreeItem(&key->prime, PR_FALSE);
-	SECITEM_ZfreeItem(&key->base, PR_FALSE);
-	SECITEM_ZfreeItem(&key->publicValue, PR_FALSE);
-	SECITEM_ZfreeItem(&key->privateValue, PR_FALSE);
-	PORT_FreeArena(arena, PR_TRUE);
+	if (!havePrivateValue) {
+	    SECITEM_ZfreeItem(&key->prime, PR_FALSE);
+	    SECITEM_ZfreeItem(&key->base, PR_FALSE);
+	    SECITEM_ZfreeItem(&key->publicValue, PR_FALSE);
+	    SECITEM_ZfreeItem(&key->privateValue, PR_FALSE);
+	    PORT_FreeArena(arena, PR_TRUE);
+	} else {
+	    SECITEM_ZfreeItem(&key->publicValue, PR_FALSE);
+	}
     }
     return rv;
 }
 
 SECStatus 
 DH_Derive(SECItem *publicValue, 
           SECItem *prime, 
           SECItem *privateValue, 
diff --git a/lib/softoken/fipstest.c b/lib/softoken/fipstest.c
--- a/lib/softoken/fipstest.c
+++ b/lib/softoken/fipstest.c
@@ -8,22 +8,25 @@
 #include "softoken.h"   /* Required for RC2-ECB, RC2-CBC, RC4, DES-ECB,  */
                         /*              DES-CBC, DES3-ECB, DES3-CBC, RSA */
                         /*              and DSA.                         */
 #include "seccomon.h"   /* Required for RSA and DSA. */
 #include "lowkeyi.h"    /* Required for RSA and DSA. */
 #include "pkcs11.h"     /* Required for PKCS #11. */
 #include "secoid.h"     /* Required for ECDSA */
 #include "secerr.h"
+#include "blapi.h"
 
 #ifndef NSS_DISABLE_ECC
 #include "ec.h"         /* Required for ECDSA */
 #endif
 
 
+#include <stdio.h>
+
 /* FIPS preprocessor directives for RC2-ECB and RC2-CBC.        */
 #define FIPS_RC2_KEY_LENGTH                      5  /*  40-bits */
 #define FIPS_RC2_ENCRYPT_LENGTH                  8  /*  64-bits */
 #define FIPS_RC2_DECRYPT_LENGTH                  8  /*  64-bits */
 
 
 /* FIPS preprocessor directives for RC4.                        */
 #define FIPS_RC4_KEY_LENGTH                      5  /*  40-bits */
@@ -2033,16 +2036,135 @@ sftk_fips_RNG_PowerUpSelfTest( void )
    if( ( rng_status != SECSuccess ) ||
        ( PORT_Memcmp( DSAX, rng_known_DSAX,
                       (FIPS_DSA_SUBPRIME_LENGTH) ) != 0 ) )
        return( CKR_DEVICE_ERROR );
        
    return( CKR_OK ); 
 }
 
+static CK_RV
+sftk_fips_DH_PowerUpSelfTest( void )
+{
+    static const PRUint8 dh_P[] = {
+	0xae,0xec,0xee,0x22,0xfa,0x3a,0xa5,0x22,
+	0xc0,0xde,0x0f,0x09,0x7e,0x17,0xc0,0x05,
+	0xf9,0xf1,0xe7,0xc6,0x87,0x14,0x6d,0x11,
+	0xe7,0xae,0xed,0x2f,0x72,0x59,0xc5,0xa9,
+	0x9b,0xb8,0x02,0xa5,0xf3,0x69,0x70,0xd6,
+	0xdd,0x90,0xf9,0x19,0x79,0xbe,0x60,0x8f,
+	0x25,0x92,0x30,0x1c,0x51,0x51,0x38,0x26,
+	0x82,0x25,0xe6,0xfc,0xed,0x65,0x96,0x8f,
+	0x57,0xe5,0x53,0x8b,0x38,0x63,0xc7,0xce,
+	0xbc,0x1b,0x4d,0x18,0x2a,0x5b,0x04,0x3f,
+	0x6a,0x3c,0x94,0x39,0xae,0x36,0xd6,0x5e,
+	0x0f,0xa2,0xcc,0xd0,0xd4,0xd5,0xc6,0x1e,
+	0xf6,0xa0,0xf5,0x89,0x4e,0xb4,0x0b,0xa4,
+	0xb3,0x2b,0x3d,0xe2,0x4e,0xe1,0x49,0x25,
+	0x99,0x5f,0x32,0x16,0x33,0x32,0x1b,0x7a,
+	0xa5,0x5c,0x6b,0x34,0x0d,0x39,0x99,0xdc,
+	0xf0,0x76,0xe5,0x5a,0xd4,0x71,0x00,0xed,
+	0x5a,0x73,0xfb,0xc8,0x01,0xad,0x99,0xcf,
+	0x99,0x52,0x7c,0x9c,0x64,0xc6,0x76,0x40,
+	0x57,0xaf,0x59,0xd7,0x38,0x0b,0x40,0xde,
+	0x33,0x0d,0xb8,0x76,0xec,0xa9,0xd8,0x73,
+	0xf8,0xef,0x26,0x66,0x06,0x27,0xdd,0x7c,
+	0xa4,0x10,0x9c,0xa6,0xaa,0xf9,0x53,0x62,
+	0x73,0x1d,0xba,0x1c,0xf1,0x67,0xf4,0x35,
+	0xed,0x6f,0x37,0x92,0xe8,0x4f,0x6c,0xba,
+	0x52,0x6e,0xa1,0xed,0xda,0x9f,0x85,0x11,
+	0x82,0x52,0x62,0x08,0x44,0xf1,0x30,0x03,
+	0xc3,0x38,0x2c,0x79,0xbd,0xd4,0x43,0x45,
+	0xee,0x8e,0x50,0xfc,0x29,0x46,0x9a,0xfe,
+	0x54,0x1a,0x19,0x8f,0x4b,0x84,0x08,0xde,
+	0x20,0x62,0x73,0xcc,0xdd,0x7e,0xf0,0xef,
+	0xa2,0xfd,0x86,0x58,0x4b,0xd8,0x37,0xeb};
+
+    static const PRUint8 dh_G[] = {
+	0x02};
+
+    static const PRUint8 dh_public[] = {
+	0xa0,0x39,0x11,0x77,0x9a,0xc1,0x30,0x1f,
+	0xbe,0x48,0xa7,0xaa,0xa0,0x84,0x54,0x64,
+	0xad,0x1b,0x70,0xfa,0x13,0x55,0x63,0xd2,
+	0x1f,0x62,0x32,0x93,0x8e,0xc9,0x3e,0x09,
+	0xa7,0x64,0xe4,0x12,0x6e,0x1b,0xf2,0x92,
+	0x3b,0xb9,0xcb,0x56,0xea,0x07,0x88,0xb5,
+	0xa6,0xbc,0x16,0x1f,0x27,0xfe,0xd8,0xaa,
+	0x40,0xb2,0xb0,0x2d,0x37,0x76,0xa6,0xa4,
+	0x82,0x2c,0x0e,0x22,0x64,0x9d,0xcb,0xd1,
+	0x00,0xb7,0x89,0x14,0x72,0x4e,0xbe,0x48,
+	0x41,0xf8,0xb2,0x51,0x11,0x09,0x4b,0x22,
+	0x01,0x23,0x39,0x96,0xe0,0x15,0xd7,0x9f,
+	0x60,0xd1,0xb7,0xae,0xfe,0x5f,0xdb,0xe7,
+	0x03,0x17,0x97,0xa6,0x16,0x74,0xbd,0x53,
+	0x81,0x19,0xc5,0x47,0x5e,0xce,0x8d,0xed,
+	0x45,0x5d,0x3c,0x00,0xa0,0x0a,0x68,0x6a,
+	0xe0,0x8e,0x06,0x46,0x6f,0xd7,0xf9,0xdf,
+	0x31,0x7e,0x77,0x44,0x0d,0x98,0xe0,0xca,
+	0x98,0x09,0x52,0x04,0x90,0xea,0x6d,0xf4,
+	0x30,0x69,0x8f,0xb1,0x9b,0xc1,0x43,0xdb,
+	0xd5,0x8d,0xc8,0x8e,0xb6,0x0b,0x05,0xbe,
+	0x0e,0xc5,0x99,0xc8,0x6e,0x4e,0xf3,0xcb,
+	0xc3,0x5e,0x9b,0x53,0xf7,0x06,0x1c,0x4f,
+	0xc7,0xb8,0x6e,0x30,0x18,0xca,0x9b,0xb9,
+	0xbc,0x5f,0x17,0x72,0x29,0x5a,0xe5,0xd9,
+	0x96,0xb7,0x0b,0xf3,0x2d,0x8c,0xf1,0xe1,
+	0x0e,0x0d,0x74,0xd5,0x9d,0xf0,0x06,0xa9,
+	0xb4,0x95,0x63,0x76,0x46,0x55,0x48,0x82,
+	0x39,0x90,0xef,0x56,0x75,0x34,0xb8,0x34,
+	0xc3,0x18,0x6e,0x1e,0xad,0xe3,0x48,0x7e,
+	0x93,0x2c,0x23,0xe7,0xf8,0x90,0x73,0xb1,
+	0x77,0x80,0x67,0xa9,0x36,0x9e,0xda,0xd2};
+
+    static const PRUint8 dh_private[] = {
+	0x0c,0x4b,0x30,0x89,0xd1,0xb8,0x62,0xcb,
+	0x3c,0x43,0x64,0x91,0xf0,0x91,0x54,0x70,
+	0xc5,0x27,0x96,0xe3,0xac,0xbe,0xe8,0x00,
+	0xec,0x55,0xf6,0xcc};
+
+    SECStatus dh_status = SECSuccess;
+
+    DHPrivateKey  dh_privkey;
+    DHPrivateKey *dh_privkey_p;
+
+    dh_privkey.prime.len  = sizeof(dh_P);
+    dh_privkey.prime.data = dh_P;
+    dh_privkey.base.len  = sizeof(dh_G);
+    dh_privkey.base.data = dh_G;
+    dh_privkey.privateValue.len  = sizeof(dh_private);
+    dh_privkey.privateValue.data = dh_private;
+    dh_privkey.arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE);
+    if (!dh_privkey.arena) {
+	dh_status = SECFailure;
+	goto cleanup;
+    }
+
+    dh_privkey_p = &dh_privkey;
+
+    /* create public key from the private one */
+    dh_status = DH_NewKey((DHParams *)ZERO_OC_POINTER, &dh_privkey_p);
+
+    if( dh_status != SECSuccess )
+	goto cleanup;
+
+    /* compare public keys */
+    if (PORT_Memcmp(dh_public, dh_privkey.publicValue.data, sizeof(dh_public)))
+	dh_status = SECFailure;
+
+cleanup:
+    PORT_FreeArena(dh_privkey.arena, PR_TRUE);
+
+    if( dh_status != SECSuccess )
+	return( CKR_DEVICE_ERROR );
+
+    return( CKR_OK );
+
+}
+
 /* FIPS selftests are implemented in library constructors and abort on failure
  * - if anybody gets to call this function it means the tests passed and this
  *   can be a successful no-op
  */
 CK_RV
 sftk_fipsPowerUpSelfTest( void )
 {
     return CKR_OK;
@@ -2152,12 +2274,18 @@ FIPS_cryptoSelftestSoftoken( void )
 #ifndef NSS_DISABLE_ECC
     /* ECDSA Power-Up SelfTest(s). */
     rv = sftk_fips_ECDSA_PowerUpSelfTest();
 
     if( rv != CKR_OK )
         return rv;
 #endif
 
+    /* DH Power-Up SelfTest(s). */
+    rv = sftk_fips_DH_PowerUpSelfTest();
+
+    if( rv != CKR_OK )
+        return rv;
+
     /* Passed Power-Up SelfTest(s). */
     return( CKR_OK );
 }
 
