pkcs11dh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
	CK_RV rv;
	CK_MECHANISM mech = { CKM_DH_PKCS_PARAMETER_GEN, NULL, 0 };
	CK_OBJECT_HANDLE domainparams = CK_INVALID_HANDLE;
	CK_OBJECT_CLASS dClass = CKO_DOMAIN_PARAMETERS;
	CK_KEY_TYPE keyType = CKK_DH;
	CK_ULONG bits = 0;
	CK_ATTRIBUTE dTemplate[] =
	{
		{ CKA_CLASS, &dClass, (CK_ULONG) sizeof(dClass) },
		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_PRIME_BITS, &bits, (CK_ULONG) sizeof(bits) }
	};
	CK_ATTRIBUTE pTemplate[] =
	{
		{ CKA_PRIME, NULL, 0 },
		{ CKA_BASE, NULL, 0 }
	};
	CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
	CK_ATTRIBUTE pubTemplate[] =
	{
		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
		{ CKA_KEY_TYPE,&keyType, (CK_ULONG) sizeof(keyType) },
		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_PRIME, NULL, 0 },
		{ CKA_BASE, NULL, 0 },
	};
	CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
	CK_ATTRIBUTE privTemplate[] =
	{
		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
		{ CKA_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) },
	};
	CK_ATTRIBUTE *attr;
	pk11_object_t *dh = NULL;
	pk11_context_t *pk11_ctx;
	isc_result_t ret;

	UNUSED(callback);

	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
						  sizeof(*pk11_ctx));
	if (pk11_ctx == NULL)
		return (ISC_R_NOMEMORY);
	ret = pk11_get_session(pk11_ctx, OP_DH, true, false,
			       false, NULL, pk11_get_best_token(OP_DH));
	if (ret != ISC_R_SUCCESS)
		goto err;

	bits = key->key_size;
	if ((generator == 0) &&
	    ((bits == 768) || (bits == 1024) || (bits == 1536))) {
		if (bits == 768) {
			pubTemplate[4].pValue =
				isc_mem_get(key->mctx, sizeof(pk11_dh_bn768));
			if (pubTemplate[4].pValue == NULL)
				DST_RET(ISC_R_NOMEMORY);
			memmove(pubTemplate[4].pValue,
				pk11_dh_bn768, sizeof(pk11_dh_bn768));
			pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn768);
		} else if (bits == 1024) {
			pubTemplate[4].pValue =
				isc_mem_get(key->mctx, sizeof(pk11_dh_bn1024));
			if (pubTemplate[4].pValue == NULL)
				DST_RET(ISC_R_NOMEMORY);
			memmove(pubTemplate[4].pValue,
				pk11_dh_bn1024, sizeof(pk11_dh_bn1024));
			pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1024);
		} else {
			pubTemplate[4].pValue =
				isc_mem_get(key->mctx, sizeof(pk11_dh_bn1536));
			if (pubTemplate[4].pValue == NULL)
				DST_RET(ISC_R_NOMEMORY);
			memmove(pubTemplate[4].pValue,
				pk11_dh_bn1536, sizeof(pk11_dh_bn1536));
			pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1536);
		}
		pubTemplate[5].pValue = isc_mem_get(key->mctx,
						    sizeof(pk11_dh_bn2));
		if (pubTemplate[5].pValue == NULL)
			DST_RET(ISC_R_NOMEMORY);
		memmove(pubTemplate[5].pValue, pk11_dh_bn2,
			sizeof(pk11_dh_bn2));
		pubTemplate[5].ulValueLen = sizeof(pk11_dh_bn2);
	} else {
		PK11_RET(pkcs_C_GenerateKey,
			 (pk11_ctx->session, &mech,
			  dTemplate, (CK_ULONG) 5, &domainparams),
			 DST_R_CRYPTOFAILURE);
		PK11_RET(pkcs_C_GetAttributeValue,
			 (pk11_ctx->session, domainparams,
			  pTemplate, (CK_ULONG) 2),
			 DST_R_CRYPTOFAILURE);
		pTemplate[0].pValue = isc_mem_get(key->mctx,
						  pTemplate[0].ulValueLen);
		if (pTemplate[0].pValue == NULL)
			DST_RET(ISC_R_NOMEMORY);
		memset(pTemplate[0].pValue, 0, pTemplate[0].ulValueLen);
		pTemplate[1].pValue = isc_mem_get(key->mctx,
						  pTemplate[1].ulValueLen);
		if (pTemplate[1].pValue == NULL)
			DST_RET(ISC_R_NOMEMORY);
		memset(pTemplate[1].pValue, 0, pTemplate[1].ulValueLen);
		PK11_RET(pkcs_C_GetAttributeValue,
			 (pk11_ctx->session, domainparams,
			  pTemplate, (CK_ULONG) 2),
			 DST_R_CRYPTOFAILURE);

		pubTemplate[4].pValue = pTemplate[0].pValue;
		pubTemplate[4].ulValueLen = pTemplate[0].ulValueLen;
		pTemplate[0].pValue = NULL;
		pubTemplate[5].pValue = pTemplate[1].pValue;
		pubTemplate[5].ulValueLen = pTemplate[1].ulValueLen;
		pTemplate[1].pValue = NULL;
	}

	mech.mechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
	PK11_RET(pkcs_C_GenerateKeyPair,
		 (pk11_ctx->session, &mech,
		  pubTemplate, (CK_ULONG) 6,
		  privTemplate, (CK_ULONG) 7,
		  &pub, &priv),
		 DST_R_CRYPTOFAILURE);

	dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
	if (dh == NULL)
		DST_RET(ISC_R_NOMEMORY);
	memset(dh, 0,  sizeof(*dh));
	key->keydata.pkey = dh;
	dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
	if (dh->repr == NULL)
		DST_RET(ISC_R_NOMEMORY);
	memset(dh->repr, 0, sizeof(*attr) * 4);
	dh->attrcnt = 4;

	attr = dh->repr;
	attr[0].type = CKA_PRIME;
	attr[0].pValue = pubTemplate[4].pValue;
	attr[0].ulValueLen = pubTemplate[4].ulValueLen;
	pubTemplate[4].pValue = NULL;

	attr[1].type = CKA_BASE;
	attr[1].pValue = pubTemplate[5].pValue;
	attr[1].ulValueLen = pubTemplate[5].ulValueLen;
	pubTemplate[5].pValue =NULL;

	attr += 2;
	attr->type = CKA_VALUE;
	PK11_RET(pkcs_C_GetAttributeValue,
		 (pk11_ctx->session, pub, attr, 1),
		 DST_R_CRYPTOFAILURE);
	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
	if (attr->pValue == NULL)
		DST_RET(ISC_R_NOMEMORY);
	memset(attr->pValue, 0, attr->ulValueLen);
	PK11_RET(pkcs_C_GetAttributeValue,
		 (pk11_ctx->session, pub, attr, 1),
		 DST_R_CRYPTOFAILURE);

	attr++;
	attr->type = CKA_VALUE;
	PK11_RET(pkcs_C_GetAttributeValue,
		 (pk11_ctx->session, priv, attr, 1),
		 DST_R_CRYPTOFAILURE);
	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
	if (attr->pValue == NULL)
		DST_RET(ISC_R_NOMEMORY);
	memset(attr->pValue, 0, attr->ulValueLen);
	PK11_RET(pkcs_C_GetAttributeValue,
		 (pk11_ctx->session, priv, attr, 1),
		 DST_R_CRYPTOFAILURE);
	attr->type = CKA_VALUE2;

	(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
	(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
	(void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams);
	pk11_return_session(pk11_ctx);
	isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));

	return (ISC_R_SUCCESS);

    err:
	pkcs11dh_destroy(key);
	if (priv != CK_INVALID_HANDLE)
		(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
	if (pub != CK_INVALID_HANDLE)
		(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
	if (domainparams != CK_INVALID_HANDLE)
		(void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams);

	if (pubTemplate[4].pValue != NULL) {
		isc_safe_memwipe(pubTemplate[4].pValue,
				 pubTemplate[4].ulValueLen);
		isc_mem_put(key->mctx,
			    pubTemplate[4].pValue,
			    pubTemplate[4].ulValueLen);
	}
	if (pubTemplate[5].pValue != NULL) {
		isc_safe_memwipe(pubTemplate[5].pValue,
				 pubTemplate[5].ulValueLen);
		isc_mem_put(key->mctx,
			    pubTemplate[5].pValue,
			    pubTemplate[5].ulValueLen);
	}
	if (pTemplate[0].pValue != NULL) {
		isc_safe_memwipe(pTemplate[0].pValue,
				 pTemplate[0].ulValueLen);
		isc_mem_put(key->mctx,
			    pTemplate[0].pValue,
			    pTemplate[0].ulValueLen);
	}
	if (pTemplate[1].pValue != NULL) {
		isc_safe_memwipe(pTemplate[1].pValue,
				 pTemplate[1].ulValueLen);
		isc_mem_put(key->mctx,
			    pTemplate[1].pValue,
			    pTemplate[1].ulValueLen);
	}

	pk11_return_session(pk11_ctx);
	isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));

	return (ret);
}