pkcs11dh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
		       isc_buffer_t *secret)
{
	CK_RV rv;
	CK_MECHANISM mech = { CKM_DH_PKCS_DERIVE, NULL, 0 };
	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
	CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
	CK_OBJECT_HANDLE hDerived = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
	CK_ATTRIBUTE *attr;
	CK_ULONG secLen;
	CK_ATTRIBUTE keyTemplate[] =
	{
		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
		{ CKA_VALUE_LEN, &secLen, (CK_ULONG) sizeof(secLen) }
	};
	CK_ATTRIBUTE valTemplate[] =
	{
		{ CKA_VALUE, NULL, 0 }
	};
	CK_BYTE *secValue;
	pk11_context_t ctx;
	isc_result_t ret;
	unsigned int i;
	isc_region_t r;

	REQUIRE(pub->keydata.pkey != NULL);
	REQUIRE(priv->keydata.pkey != NULL);
	REQUIRE(priv->keydata.pkey->repr != NULL);
	attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_PRIME);
	if (attr == NULL)
		return (DST_R_INVALIDPUBLICKEY);
	REQUIRE(attr != NULL);
	secLen = attr->ulValueLen;
	attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE);
	if (attr == NULL)
		return (DST_R_INVALIDPUBLICKEY);

	ret = pk11_get_session(&ctx, OP_DH, true, false,
			       priv->keydata.pkey->reqlogon, NULL,
			       pk11_get_best_token(OP_DH));
	if (ret != ISC_R_SUCCESS)
		return (ret);

	mech.ulParameterLen = attr->ulValueLen;
	mech.pParameter = isc_mem_get(pub->mctx, mech.ulParameterLen);
	if (mech.pParameter == NULL)
		DST_RET(ISC_R_NOMEMORY);
	memmove(mech.pParameter, attr->pValue, mech.ulParameterLen);

	ret = pkcs11dh_loadpriv(priv, ctx.session, &hKey);
	if (ret != ISC_R_SUCCESS)
		goto err;

	PK11_RET(pkcs_C_DeriveKey,
		 (ctx.session, &mech, hKey,
		  keyTemplate, (CK_ULONG) 6, &hDerived),
		 DST_R_COMPUTESECRETFAILURE);

	attr = valTemplate;
	PK11_RET(pkcs_C_GetAttributeValue,
		 (ctx.session, hDerived, attr, (CK_ULONG) 1),
		 DST_R_CRYPTOFAILURE);
	attr->pValue = isc_mem_get(pub->mctx, attr->ulValueLen);
	if (attr->pValue == NULL)
		DST_RET(ISC_R_NOMEMORY);
	memset(attr->pValue, 0, attr->ulValueLen);
	PK11_RET(pkcs_C_GetAttributeValue,
		 (ctx.session, hDerived, attr, (CK_ULONG) 1),
		 DST_R_CRYPTOFAILURE);

	/* strip leading zeros */
	secValue = (CK_BYTE_PTR) attr->pValue;
	for (i = 0; i < attr->ulValueLen; i++)
		if (secValue[i] != 0)
			break;
	isc_buffer_availableregion(secret, &r);
	if (r.length < attr->ulValueLen - i)
		DST_RET(ISC_R_NOSPACE);
	memmove(r.base, secValue + i, attr->ulValueLen - i);
	isc_buffer_add(secret, attr->ulValueLen - i);
	ret = ISC_R_SUCCESS;

    err:
	if (hDerived != CK_INVALID_HANDLE)
		(void) pkcs_C_DestroyObject(ctx.session, hDerived);
	if (valTemplate[0].pValue != NULL) {
		isc_safe_memwipe(valTemplate[0].pValue,
				 valTemplate[0].ulValueLen);
		isc_mem_put(pub->mctx,
			    valTemplate[0].pValue,
			    valTemplate[0].ulValueLen);
	}
	if ((hKey != CK_INVALID_HANDLE) && !priv->keydata.pkey->ontoken)
		(void) pkcs_C_DestroyObject(ctx.session, hKey);
	if (mech.pParameter != NULL) {
		isc_safe_memwipe(mech.pParameter, mech.ulParameterLen);
		isc_mem_put(pub->mctx, mech.pParameter, mech.ulParameterLen);
	}
	pk11_return_session(&ctx);
	return (ret);
}