/*
 * Copyright (c) 2012 SURFnet
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
 ObjectTests.cpp

 Contains test cases for:
	 C_CreateObject
	 C_CopyObject
	 C_DestroyObject
	 C_GetAttributeValue
	 C_SetAttributeValue
	 C_FindObjectsInit
	 C_FindObjects
	 C_FindObjectsFinal
	 C_GenererateKeyPair

 Below is a list of tests we need to add in order to verify that the PKCS#11 library
 is working as expected.

 We want to be sure that order of attributes does not impact the tests, therefore
 every function involving attributes should have the order of the attributes
 in the template randomized.

 We want to be sure that only attributes that are specified as being part of an
 object class can be used when creating an object.
 Using other attributes should return an error on creation of the object.

 We want to be sure that attributes that are required but missing will result
 in a template incomplete return value.

 We want to be sure that we get an error when trying to modify an attribute that
 may not be modified

 We want to be sure that attributes that may be changed to one value but not
 back to the previous value are handled correctly.

 We want to verify that an error is returned when we are trying to modify
 read-only attributes.

 We want to verify that sensitive attributes cannot be read.

 Because the teardown also removes token objects it is not really
 required to destroy objects created during the test in the CreateObject tests.

 *****************************************************************************/

#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "ObjectTests.h"

// Common object attributes
const CK_BBOOL CKA_TOKEN_DEFAULT = CK_FALSE;
//const CK_BBOOL CKA_PRIVATE_DEFAULT = <token/object attribute dependent>
const CK_BBOOL CKA_MODIFIABLE_DEFAULT = CK_TRUE;
const CK_UTF8CHAR_PTR CKA_LABEL_DEFAULT = NULL;
const CK_BBOOL CKA_COPYABLE_DEFAULT = CK_TRUE;
const CK_BBOOL CKA_DESTROYABLE_DEFAULT = CK_TRUE;

// Data Object Attributes
const CK_UTF8CHAR_PTR CKA_APPLICATION_DEFAULT = NULL;
const CK_BYTE_PTR CKA_OBJECT_ID_DEFAULT = NULL;
const CK_BYTE_PTR CKA_VALUE_DEFAULT = NULL;

// CKA_TOKEN
const CK_BBOOL ON_TOKEN = CK_TRUE;
const CK_BBOOL IN_SESSION = CK_FALSE;

// CKA_PRIVATE
const CK_BBOOL IS_PRIVATE = CK_TRUE;
const CK_BBOOL IS_PUBLIC = CK_FALSE;


CPPUNIT_TEST_SUITE_REGISTRATION(ObjectTests);

void ObjectTests::checkCommonObjectAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_OBJECT_CLASS objClass)
{
	CK_RV rv;

	CK_OBJECT_CLASS obj_class = CKO_VENDOR_DEFINED;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_CLASS, &obj_class, sizeof(obj_class) }
	};

	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(obj_class == objClass);
}

void ObjectTests::checkCommonStorageObjectAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_BBOOL bToken, CK_BBOOL /*bPrivate*/, CK_BBOOL bModifiable, CK_UTF8CHAR_PTR pLabel, CK_ULONG ulLabelLen, CK_BBOOL bCopyable, CK_BBOOL bDestroyable)
{
	CK_RV rv;

	CK_BBOOL obj_token = CK_FALSE;
	CK_BBOOL obj_private = CK_FALSE;
	CK_BBOOL obj_modifiable = CK_FALSE;
	CK_BBOOL obj_copyable = CK_FALSE;
	CK_BBOOL obj_destroyable = CK_FALSE;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_LABEL, NULL_PTR, 0 },
		{ CKA_TOKEN, &obj_token, sizeof(obj_token) },
		{ CKA_PRIVATE, &obj_private, sizeof(obj_private) },
		{ CKA_MODIFIABLE, &obj_modifiable, sizeof(obj_modifiable) },
		{ CKA_COPYABLE, &obj_copyable, sizeof(obj_copyable) },
		{ CKA_DESTROYABLE, &obj_destroyable, sizeof(obj_destroyable) }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 6) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulLabelLen);
	CPPUNIT_ASSERT(obj_token == bToken);
	/* Default is token-specifict
	CPPUNIT_ASSERT(obj_private == bPrivate); */
	CPPUNIT_ASSERT(obj_modifiable == bModifiable);
	CPPUNIT_ASSERT(obj_copyable == bCopyable);
	CPPUNIT_ASSERT(obj_destroyable == bDestroyable);
	if (ulLabelLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pLabel, ulLabelLen) == 0);

	free(attribs[0].pValue);
}

void ObjectTests::checkDataObjectAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_UTF8CHAR_PTR pApplication, CK_ULONG ulApplicationLen, CK_BYTE_PTR pObjectID, CK_ULONG ulObjectIdLen, CK_BYTE_PTR pValue, CK_ULONG ulValueLen)
{
	CK_RV rv;

	CK_ATTRIBUTE attribs[] = {
		{ CKA_APPLICATION, NULL_PTR, 0 },
		{ CKA_OBJECT_ID, NULL_PTR, 0 },
		{ CKA_VALUE, NULL_PTR, 0 }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 3) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);
	attribs[1].pValue = (CK_VOID_PTR)malloc(attribs[1].ulValueLen);
	attribs[2].pValue = (CK_VOID_PTR)malloc(attribs[2].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 3) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulApplicationLen);
	CPPUNIT_ASSERT(attribs[1].ulValueLen == ulObjectIdLen);
	CPPUNIT_ASSERT(attribs[2].ulValueLen == ulValueLen);
	if (ulApplicationLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pApplication, ulApplicationLen) == 0);
	if (ulObjectIdLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[1].pValue, pObjectID, ulObjectIdLen) == 0);
	if (ulValueLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[2].pValue, pValue, ulValueLen) == 0);

	free(attribs[0].pValue);
	free(attribs[1].pValue);
	free(attribs[2].pValue);
}

void ObjectTests::checkCommonCertificateObjectAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_CERTIFICATE_TYPE certType, CK_BBOOL bTrusted, CK_ULONG ulCertificateCategory, CK_BYTE_PTR pCheckValue, CK_ULONG ulCheckValueLen, CK_DATE startDate, CK_ULONG ulStartDateLen, CK_DATE endDate, CK_ULONG ulEndDateLen)
{
	CK_RV rv;

	CK_CERTIFICATE_TYPE obj_type = CKC_X_509;
	CK_BBOOL obj_trusted = CK_FALSE;
	CK_ULONG obj_category = 0;
	CK_DATE obj_start;
	CK_DATE obj_end;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_CHECK_VALUE, NULL_PTR, 0 },
		{ CKA_CERTIFICATE_TYPE, &obj_type, sizeof(obj_type) },
		{ CKA_TRUSTED, &obj_trusted, sizeof(obj_trusted) },
		{ CKA_CERTIFICATE_CATEGORY, &obj_category, sizeof(obj_category) },
		{ CKA_START_DATE, &obj_start, sizeof(obj_start) },
		{ CKA_END_DATE, &obj_end, sizeof(obj_end) }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 6) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulCheckValueLen);
	CPPUNIT_ASSERT(obj_type == certType);
	CPPUNIT_ASSERT(obj_trusted == bTrusted);
	CPPUNIT_ASSERT(obj_category == ulCertificateCategory);
	CPPUNIT_ASSERT(attribs[4].ulValueLen == ulStartDateLen);
	CPPUNIT_ASSERT(attribs[5].ulValueLen == ulEndDateLen);
	if (ulCheckValueLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pCheckValue, ulCheckValueLen) == 0);
	if (ulStartDateLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[4].pValue, &startDate, ulStartDateLen) == 0);
	if (ulEndDateLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[5].pValue, &endDate, ulEndDateLen) == 0);

	free(attribs[0].pValue);
}

void ObjectTests::checkX509CertificateObjectAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_BYTE_PTR pSubject, CK_ULONG ulSubjectLen, CK_BYTE_PTR pId, CK_ULONG ulIdLen, CK_BYTE_PTR pIssuer, CK_ULONG ulIssuerLen, CK_BYTE_PTR pSerialNumber, CK_ULONG ulSerialNumberLen, CK_BYTE_PTR pValue, CK_ULONG ulValueLen, CK_BYTE_PTR pUrl, CK_ULONG ulUrlLen, CK_BYTE_PTR pHashOfSubjectPublicKey, CK_ULONG ulHashOfSubjectPublicKeyLen, CK_BYTE_PTR pHashOfIssuerPublicKey, CK_ULONG ulHashOfIssuerPublicKeyLen, CK_ULONG ulJavaMidpSecurityDomain, CK_MECHANISM_TYPE nameHashAlgorithm)
{
	CK_RV rv;

	CK_ULONG obj_java = 0;
	CK_MECHANISM_TYPE obj_mech = CKM_VENDOR_DEFINED;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_SUBJECT, NULL_PTR, 0 },
		{ CKA_ID, NULL_PTR, 0 },
		{ CKA_ISSUER, NULL_PTR, 0 },
		{ CKA_SERIAL_NUMBER, NULL_PTR, 0 },
		{ CKA_VALUE, NULL_PTR, 0 },
		{ CKA_URL, NULL_PTR, 0 },
		{ CKA_HASH_OF_SUBJECT_PUBLIC_KEY, NULL_PTR, 0 },
		{ CKA_HASH_OF_ISSUER_PUBLIC_KEY, NULL_PTR, 0 },
		{ CKA_JAVA_MIDP_SECURITY_DOMAIN, &obj_java, sizeof(obj_java) },
		{ CKA_NAME_HASH_ALGORITHM, &obj_mech, sizeof(obj_mech) }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 8) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);
	attribs[1].pValue = (CK_VOID_PTR)malloc(attribs[1].ulValueLen);
	attribs[2].pValue = (CK_VOID_PTR)malloc(attribs[2].ulValueLen);
	attribs[3].pValue = (CK_VOID_PTR)malloc(attribs[3].ulValueLen);
	attribs[4].pValue = (CK_VOID_PTR)malloc(attribs[4].ulValueLen);
	attribs[5].pValue = (CK_VOID_PTR)malloc(attribs[5].ulValueLen);
	attribs[6].pValue = (CK_VOID_PTR)malloc(attribs[6].ulValueLen);
	attribs[7].pValue = (CK_VOID_PTR)malloc(attribs[7].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 10) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulSubjectLen);
	CPPUNIT_ASSERT(attribs[1].ulValueLen == ulIdLen);
	CPPUNIT_ASSERT(attribs[2].ulValueLen == ulIssuerLen);
	CPPUNIT_ASSERT(attribs[3].ulValueLen == ulSerialNumberLen);
	CPPUNIT_ASSERT(attribs[4].ulValueLen == ulValueLen);
	CPPUNIT_ASSERT(attribs[5].ulValueLen == ulUrlLen);
	CPPUNIT_ASSERT(attribs[6].ulValueLen == ulHashOfSubjectPublicKeyLen);
	CPPUNIT_ASSERT(attribs[7].ulValueLen == ulHashOfIssuerPublicKeyLen);
	CPPUNIT_ASSERT(obj_java == ulJavaMidpSecurityDomain);
	CPPUNIT_ASSERT(obj_mech == nameHashAlgorithm);
	if (ulSubjectLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pSubject, ulSubjectLen) == 0);
	if (ulIdLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[1].pValue, pId, ulIdLen) == 0);
	if (ulIssuerLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[2].pValue, pIssuer, ulIssuerLen) == 0);
	if (ulSerialNumberLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[3].pValue, pSerialNumber, ulSerialNumberLen) == 0);
	if (ulValueLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[4].pValue, pValue, ulValueLen) == 0);
	if (ulUrlLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[5].pValue, pUrl, ulUrlLen) == 0);
	if (ulHashOfSubjectPublicKeyLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[6].pValue, pHashOfSubjectPublicKey, ulHashOfSubjectPublicKeyLen) == 0);
	if (ulHashOfIssuerPublicKeyLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[7].pValue, pHashOfIssuerPublicKey, ulHashOfIssuerPublicKeyLen) == 0);

	free(attribs[0].pValue);
	free(attribs[1].pValue);
	free(attribs[2].pValue);
	free(attribs[3].pValue);
	free(attribs[4].pValue);
	free(attribs[5].pValue);
	free(attribs[6].pValue);
	free(attribs[7].pValue);
}

void ObjectTests::checkCommonKeyAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_KEY_TYPE keyType, CK_BYTE_PTR pId, CK_ULONG ulIdLen, CK_DATE startDate, CK_ULONG ulStartDateLen, CK_DATE endDate, CK_ULONG ulEndDateLen, CK_BBOOL bDerive, CK_BBOOL bLocal, CK_MECHANISM_TYPE keyMechanismType, CK_MECHANISM_TYPE_PTR pAllowedMechanisms, CK_ULONG ulAllowedMechanismsLen)
{
	CK_RV rv;

	CK_KEY_TYPE obj_type = CKK_VENDOR_DEFINED;
	CK_DATE obj_start;
	CK_DATE obj_end;
	CK_BBOOL obj_derive = CK_FALSE;
	CK_BBOOL obj_local = CK_FALSE;
	CK_MECHANISM_TYPE obj_mech = CKM_VENDOR_DEFINED;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_ID, NULL_PTR, 0 },
		{ CKA_KEY_TYPE, &obj_type, sizeof(obj_type) },
		{ CKA_START_DATE, &obj_start, sizeof(obj_start) },
		{ CKA_END_DATE, &obj_end, sizeof(obj_end) },
		{ CKA_DERIVE, &obj_derive, sizeof(obj_derive) },
		{ CKA_LOCAL, &obj_local, sizeof(obj_local) },
		{ CKA_KEY_GEN_MECHANISM, &obj_mech, sizeof(obj_mech) },
		{ CKA_ALLOWED_MECHANISMS, NULL_PTR, 0 }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 8) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulIdLen);
	CPPUNIT_ASSERT(obj_type == keyType);
	CPPUNIT_ASSERT(attribs[2].ulValueLen == ulStartDateLen);
	CPPUNIT_ASSERT(attribs[3].ulValueLen == ulEndDateLen);
	CPPUNIT_ASSERT(obj_derive == bDerive);
	CPPUNIT_ASSERT(obj_local == bLocal);
	CPPUNIT_ASSERT(obj_mech == keyMechanismType);
	CPPUNIT_ASSERT(attribs[7].ulValueLen == ulAllowedMechanismsLen);

	if (ulIdLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pId, ulIdLen) == 0);
	if (ulStartDateLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[2].pValue, &startDate, ulStartDateLen) == 0);
	if (ulEndDateLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[3].pValue, &endDate, ulEndDateLen) == 0);
	if (ulAllowedMechanismsLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[7].pValue, pAllowedMechanisms, ulAllowedMechanismsLen) == 0);

	free(attribs[0].pValue);
}

void ObjectTests::checkCommonPublicKeyAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_BYTE_PTR pSubject, CK_ULONG ulSubjectLen, CK_BBOOL /*bEncrypt*/, CK_BBOOL /*bVerify*/, CK_BBOOL /*bVerifyRecover*/, CK_BBOOL /*bWrap*/, CK_BBOOL bTrusted, CK_ATTRIBUTE_PTR pWrapTemplate, CK_ULONG ulWrapTemplateLen)
{
	CK_RV rv;

	CK_BBOOL obj_encrypt = CK_FALSE;
	CK_BBOOL obj_verify = CK_FALSE;
	CK_BBOOL obj_verify_recover = CK_FALSE;
	CK_BBOOL obj_wrap = CK_FALSE;
	CK_BBOOL obj_trusted = CK_FALSE;
	CK_LONG len_wrap_template = ulWrapTemplateLen;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_SUBJECT, NULL_PTR, 0 },
		{ CKA_ENCRYPT, &obj_encrypt, sizeof(obj_encrypt) },
		{ CKA_VERIFY, &obj_verify, sizeof(obj_verify) },
		{ CKA_VERIFY_RECOVER, &obj_verify_recover, sizeof(obj_verify_recover) },
		{ CKA_WRAP, &obj_wrap, sizeof(obj_wrap) },
		{ CKA_TRUSTED, &obj_trusted, sizeof(obj_trusted) },
		{ CKA_WRAP_TEMPLATE, pWrapTemplate, ulWrapTemplateLen }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 7) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulSubjectLen);
	/* Default is token-specifict
	CPPUNIT_ASSERT(obj_encrypt == bEncrypt);
	CPPUNIT_ASSERT(obj_verify == bVerify);
	CPPUNIT_ASSERT(obj_verify_recover == bVerifyRecover);
	CPPUNIT_ASSERT(obj_wrap == bWrap); */
	CPPUNIT_ASSERT(obj_trusted == bTrusted);
	len_wrap_template = attribs[6].ulValueLen;
	CPPUNIT_ASSERT(len_wrap_template == 0);
	if (ulSubjectLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pSubject, ulSubjectLen) == 0);

	free(attribs[0].pValue);
}

void ObjectTests::checkCommonPrivateKeyAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_BYTE_PTR pSubject, CK_ULONG ulSubjectLen, CK_BBOOL bSensitive, CK_BBOOL bDecrypt, CK_BBOOL bSign, CK_BBOOL bSignRecover, CK_BBOOL bUnwrap, CK_BBOOL bExtractable, CK_BBOOL bAlwaysSensitive, CK_BBOOL bNeverExtractable, CK_BBOOL bWrapWithTrusted, CK_ATTRIBUTE_PTR pUnwrapTemplate, CK_ULONG ulUnwrapTemplateLen, CK_BBOOL bAlwaysAuthenticate)
{
	CK_RV rv;

	CK_BBOOL obj_sensitive = CK_FALSE;
	CK_BBOOL obj_decrypt = CK_FALSE;
	CK_BBOOL obj_sign = CK_FALSE;
	CK_BBOOL obj_sign_recover = CK_FALSE;
	CK_BBOOL obj_unwrap = CK_FALSE;
	CK_BBOOL obj_extractable = CK_FALSE;
	CK_BBOOL obj_always_sensitive = CK_FALSE;
	CK_BBOOL obj_never_extractable = CK_FALSE;
	CK_BBOOL obj_wrap_with_trusted = CK_FALSE;
	CK_BBOOL obj_always_authenticate = CK_FALSE;
	CK_LONG len_unwrap_template = ulUnwrapTemplateLen;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_SUBJECT, NULL_PTR, 0 },
		{ CKA_SENSITIVE, &obj_sensitive, sizeof(obj_sensitive) },
		{ CKA_DECRYPT, &obj_decrypt, sizeof(obj_decrypt) },
		{ CKA_SIGN, &obj_sign, sizeof(obj_sign) },
		{ CKA_SIGN_RECOVER, &obj_sign_recover, sizeof(obj_sign_recover) },
		{ CKA_UNWRAP, &obj_unwrap, sizeof(obj_unwrap) },
		{ CKA_EXTRACTABLE, &obj_extractable, sizeof(obj_extractable) },
		{ CKA_ALWAYS_SENSITIVE, &obj_always_sensitive, sizeof(obj_always_sensitive) },
		{ CKA_NEVER_EXTRACTABLE, &obj_never_extractable, sizeof(obj_never_extractable) },
		{ CKA_WRAP_WITH_TRUSTED, &obj_wrap_with_trusted, sizeof(obj_wrap_with_trusted) },
		{ CKA_UNWRAP_TEMPLATE, pUnwrapTemplate, ulUnwrapTemplateLen },
		{ CKA_ALWAYS_AUTHENTICATE, &obj_always_authenticate, sizeof(obj_always_authenticate) }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 12) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulSubjectLen);
	CPPUNIT_ASSERT(obj_sensitive == bSensitive);
	CPPUNIT_ASSERT(obj_decrypt == bDecrypt);
	CPPUNIT_ASSERT(obj_sign == bSign);
	CPPUNIT_ASSERT(obj_sign_recover == bSignRecover);
	CPPUNIT_ASSERT(obj_unwrap == bUnwrap);
	CPPUNIT_ASSERT(obj_extractable == bExtractable);
	CPPUNIT_ASSERT(obj_always_sensitive == bAlwaysSensitive);
	CPPUNIT_ASSERT(obj_never_extractable == bNeverExtractable);
	CPPUNIT_ASSERT(obj_wrap_with_trusted == bWrapWithTrusted);
	CPPUNIT_ASSERT(obj_always_authenticate == bAlwaysAuthenticate);
	len_unwrap_template = attribs[10].ulValueLen;
	CPPUNIT_ASSERT(len_unwrap_template == 0);
	if (ulSubjectLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pSubject, ulSubjectLen) == 0);

	free(attribs[0].pValue);
}

void ObjectTests::checkCommonRSAPublicKeyAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_BYTE_PTR pModulus, CK_ULONG ulModulusLen, CK_ULONG ulModulusBits, CK_BYTE_PTR pPublicExponent, CK_ULONG ulPublicExponentLen)
{
	CK_RV rv;

	CK_ULONG obj_bits = 0;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_MODULUS, NULL_PTR, 0 },
		{ CKA_PUBLIC_EXPONENT, NULL_PTR, 0 },
		{ CKA_MODULUS_BITS, &obj_bits, sizeof(obj_bits) }
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 2) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);
	attribs[1].pValue = (CK_VOID_PTR)malloc(attribs[1].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 3) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulModulusLen);
	CPPUNIT_ASSERT(attribs[1].ulValueLen == ulPublicExponentLen);
	CPPUNIT_ASSERT(obj_bits == ulModulusBits);
	if (ulModulusLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pModulus, ulModulusLen) == 0);
	if (ulPublicExponentLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[1].pValue, pPublicExponent, ulPublicExponentLen) == 0);

	free(attribs[0].pValue);
	free(attribs[1].pValue);
}

void ObjectTests::checkCommonRSAPrivateKeyAttributes(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_BYTE_PTR pModulus, CK_ULONG ulModulusLen, CK_BYTE_PTR /*pPublicExponent*/, CK_ULONG /*ulPublicExponentLen*/, CK_BYTE_PTR pPrivateExponent, CK_ULONG ulPrivateExponentLen, CK_BYTE_PTR /*pPrime1*/, CK_ULONG /*ulPrime1Len*/, CK_BYTE_PTR /*pPrime2*/, CK_ULONG /*ulPrime2Len*/, CK_BYTE_PTR /*pExponent1*/, CK_ULONG /*ulExponent1Len*/, CK_BYTE_PTR /*pExponent2*/, CK_ULONG /*ulExponent2Len*/, CK_BYTE_PTR /*pCoefficient*/, CK_ULONG /*ulCoefficientLen*/)
{
	CK_RV rv;

	CK_ATTRIBUTE attribs[] = {
		{ CKA_MODULUS, NULL_PTR, 0 },
		{ CKA_PRIVATE_EXPONENT, NULL_PTR, 0 }
		/* Some tokens may only store modulus and private exponent
		{ CKA_PUBLIC_EXPONENT, NULL_PTR, 0 },
		{ CKA_PRIME_1, NULL_PTR, 0 },
		{ CKA_PRIME_2, NULL_PTR, 0 },
		{ CKA_EXPONENT_1, NULL_PTR, 0 },
		{ CKA_EXPONENT_2, NULL_PTR, 0 },
		{ CKA_COEFFICIENT, NULL_PTR, 0 }, */
	};

	// Get length
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 2) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	attribs[0].pValue = (CK_VOID_PTR)malloc(attribs[0].ulValueLen);
	attribs[1].pValue = (CK_VOID_PTR)malloc(attribs[1].ulValueLen);

	// Check values
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &attribs[0], 2) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == ulModulusLen);
	CPPUNIT_ASSERT(attribs[1].ulValueLen == ulPrivateExponentLen);
	if (ulModulusLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[0].pValue, pModulus, ulModulusLen) == 0);
	if (ulPrivateExponentLen > 0)
		CPPUNIT_ASSERT(memcmp(attribs[1].pValue, pPrivateExponent, ulPrivateExponentLen) == 0);

	free(attribs[0].pValue);
	free(attribs[1].pValue);
}

CK_RV ObjectTests::createDataObjectMinimal(CK_SESSION_HANDLE hSession, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hObject)
{
	CK_OBJECT_CLASS cClass = CKO_DATA;
	CK_UTF8CHAR label[] = "A data object";
	CK_ATTRIBUTE objTemplate[] = {
		// Common
		{ CKA_CLASS, &cClass, sizeof(cClass) },

		// Storage
		{ CKA_TOKEN, &bToken, sizeof(bToken) },
		{ CKA_PRIVATE, &bPrivate, sizeof(bPrivate) },
		//CKA_MODIFIABLE
		{ CKA_LABEL, label, sizeof(label)-1 },
		//CKA_COPYABLE
		//CKA_DESTROYABLE

		// Data
	 };

	hObject = CK_INVALID_HANDLE;
	return CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE),&hObject) );
}

CK_RV ObjectTests::createDataObjectMCD(CK_SESSION_HANDLE hSession, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_BBOOL bModifiable, CK_BBOOL bCopyable, CK_BBOOL bDestroyable, CK_OBJECT_HANDLE &hObject)
{
	CK_OBJECT_CLASS cClass = CKO_DATA;
	CK_UTF8CHAR label[] = "A data object";
	CK_ATTRIBUTE objTemplate[] = {
		// Common
		{ CKA_CLASS, &cClass, sizeof(cClass) },

		// Storage
		{ CKA_TOKEN, &bToken, sizeof(bToken) },
		{ CKA_PRIVATE, &bPrivate, sizeof(bPrivate) },
		{ CKA_MODIFIABLE, &bModifiable, sizeof(bModifiable) },
		{ CKA_LABEL, label, sizeof(label)-1 },
		{ CKA_COPYABLE, &bCopyable, sizeof(bCopyable) },
		{ CKA_DESTROYABLE, &bDestroyable, sizeof(bDestroyable) }

		// Data
	 };

	hObject = CK_INVALID_HANDLE;
	return CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE),&hObject) );
}

CK_RV ObjectTests::createDataObjectNormal(CK_SESSION_HANDLE hSession, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hObject)
{
	CK_OBJECT_CLASS cClass = CKO_DATA;
	CK_UTF8CHAR label[] = "A data object";

	CK_UTF8CHAR application[] = "An application";
	CK_BYTE objectID[] = "invalid object id";
	CK_BYTE data[] = "Sample data";

	CK_ATTRIBUTE objTemplate[] = {
		// Common
		{ CKA_CLASS, &cClass, sizeof(cClass) },

		// Storage
		{ CKA_TOKEN, &bToken, sizeof(bToken) },
		{ CKA_PRIVATE, &bPrivate, sizeof(bPrivate) },
		//CKA_MODIFIABLE
		{ CKA_LABEL, label, sizeof(label)-1 },
		//CKA_COPYABLE
		//CKA_DESTROYABLE

		// Data
		{ CKA_APPLICATION, application, sizeof(application)-1 },
		{ CKA_OBJECT_ID, objectID, sizeof(objectID) },
		{ CKA_VALUE, data, sizeof(data) }
	};

	hObject = CK_INVALID_HANDLE;
	return CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE),&hObject) );
}

CK_RV ObjectTests::createCertificateObjectIncomplete(CK_SESSION_HANDLE hSession, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hObject)
{
	CK_OBJECT_CLASS cClass = CKO_CERTIFICATE;
	CK_ATTRIBUTE objTemplate[] = {
		// Common
		{ CKA_CLASS, &cClass, sizeof(cClass) },
		// Storage
		{ CKA_TOKEN, &bToken, sizeof(bToken) },
		{ CKA_PRIVATE, &bPrivate, sizeof(bPrivate) }
	};

	hObject = CK_INVALID_HANDLE;
	return CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE),&hObject) );
}

CK_RV ObjectTests::createCertificateObjectX509(CK_SESSION_HANDLE hSession, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hObject)
{
	CK_OBJECT_CLASS cClass = CKO_CERTIFICATE;
	CK_CERTIFICATE_TYPE cType = CKC_X_509;
	const char *pSubject = "invalid subject der";
	const char *pValue = "invalid certificate der";

	CK_ATTRIBUTE objTemplate[] = {
		// Common
		{ CKA_CLASS, &cClass, sizeof(cClass) },
		// Storage
		{ CKA_TOKEN, &bToken, sizeof(bToken) },
		{ CKA_PRIVATE, &bPrivate, sizeof(bPrivate) },
		// Common Certificate Object Attributes
		{ CKA_CERTIFICATE_TYPE, &cType, sizeof(cType) },
		// X.509 Certificate Object Attributes
		{ CKA_SUBJECT, (CK_VOID_PTR)pSubject, strlen(pSubject) },
		{ CKA_VALUE, (CK_VOID_PTR)pValue, strlen(pValue) }
	};

	hObject = CK_INVALID_HANDLE;
	return CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE),&hObject) );
}

CK_RV ObjectTests::generateRsaKeyPair(CK_SESSION_HANDLE hSession, CK_BBOOL bTokenPuk, CK_BBOOL bPrivatePuk, CK_BBOOL bTokenPrk, CK_BBOOL bPrivatePrk, CK_OBJECT_HANDLE &hPuk, CK_OBJECT_HANDLE &hPrk)
{
	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 };
	CK_ULONG bits = 1536;
	CK_BYTE pubExp[] = {0x01, 0x00, 0x01};
	CK_BYTE subject[] = { 0x12, 0x34 }; // dummy
	CK_BYTE id[] = { 123 } ; // dummy
	CK_BBOOL bFalse = CK_FALSE;
	CK_BBOOL bTrue = CK_TRUE;
	CK_ATTRIBUTE pukAttribs[] = {
		{ CKA_TOKEN, &bTokenPuk, sizeof(bTokenPuk) },
		{ CKA_PRIVATE, &bPrivatePuk, sizeof(bPrivatePuk) },
		{ CKA_ENCRYPT, &bFalse, sizeof(bFalse) },
		{ CKA_VERIFY, &bTrue, sizeof(bTrue) },
		{ CKA_WRAP, &bFalse, sizeof(bFalse) },
		{ CKA_MODULUS_BITS, &bits, sizeof(bits) },
		{ CKA_PUBLIC_EXPONENT, &pubExp[0], sizeof(pubExp) }
	};
	CK_ATTRIBUTE prkAttribs[] = {
		{ CKA_TOKEN, &bTokenPrk, sizeof(bTokenPrk) },
		{ CKA_PRIVATE, &bPrivatePrk, sizeof(bPrivatePrk) },
		{ CKA_SUBJECT, &subject[0], sizeof(subject) },
		{ CKA_ID, &id[0], sizeof(id) },
		{ CKA_SENSITIVE, &bTrue, sizeof(bTrue) },
		{ CKA_DECRYPT, &bFalse, sizeof(bFalse) },
		{ CKA_SIGN, &bTrue, sizeof(bTrue) },
		{ CKA_UNWRAP, &bFalse, sizeof(bFalse) }
	};

	hPuk = CK_INVALID_HANDLE;
	hPrk = CK_INVALID_HANDLE;
	return CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism,
							 pukAttribs, sizeof(pukAttribs)/sizeof(CK_ATTRIBUTE),
							 prkAttribs, sizeof(prkAttribs)/sizeof(CK_ATTRIBUTE),
							 &hPuk, &hPrk) );
}

void ObjectTests::testCreateObject()
{
//    printf("\ntestCreateObject\n");

	// [PKCS#11 v2.40, C_CreateObject]
	// a. Only session objects can be created during read-only session.
	// b. Only public objects can be created unless the normal user is logged in.
	// c. Key object will have CKA_LOCAL == CK_FALSE.
	// d. If key object is secret or a private key then both CKA_ALWAYS_SENSITIVE == CK_FALSE and CKA_NEVER_EXTRACTABLE == CKA_FALSE.

	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject;

	CK_BBOOL bFalse = CK_FALSE;
	CK_BBOOL bTrue = CK_TRUE;
	CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY;
	CK_KEY_TYPE genKeyType = CKK_GENERIC_SECRET;
	CK_BYTE keyPtr[128];
	CK_ULONG keyLen = 128;
	CK_MECHANISM_TYPE allowedMechs[] = {
		CKM_RSA_PKCS_PSS,
		CKM_SHA256_RSA_PKCS_PSS
	};
	CK_ATTRIBUTE attribs[] = {
		{ CKA_EXTRACTABLE, &bFalse, sizeof(bFalse) },
		{ CKA_CLASS, &secretClass, sizeof(secretClass) },
		{ CKA_KEY_TYPE, &genKeyType, sizeof(genKeyType) },
		{ CKA_TOKEN, &bFalse, sizeof(bFalse) },
		{ CKA_PRIVATE, &bTrue, sizeof(bTrue) },
		{ CKA_SENSITIVE, &bTrue, sizeof(bTrue) },
		{ CKA_VALUE, keyPtr, keyLen },
		{ CKA_ALLOWED_MECHANISMS, &allowedMechs, sizeof(allowedMechs) }
	};

	CK_BBOOL local;
	CK_BBOOL always;
	CK_BBOOL never;
	CK_MECHANISM_TYPE mechs[2] = {};
	CK_ATTRIBUTE getTemplate[] = {
		{ CKA_LOCAL, &local, sizeof(local) },
		{ CKA_ALWAYS_SENSITIVE, &always, sizeof(always) },
		{ CKA_NEVER_EXTRACTABLE, &never, sizeof(never) },
		{ CKA_ALLOWED_MECHANISMS, &mechs, sizeof(mechs) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	/////////////////////////////////
	// READ-ONLY & PUBLIC
	/////////////////////////////////

	// Open read-only session and don't login
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should be allowed to create public session objects
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Only public objects can be created unless the normal user is logged in
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);

	// We should not be allowed to create token objects because the session is read-only
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);

	/////////////////////////////////
	// READ-ONLY & USER
	/////////////////////////////////

	// Login USER into the read-only session
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// We should be allowed to create public session objects
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should be allowed to create private session objects
	rv  = createDataObjectMinimal(hSession, IN_SESSION, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should not be allowed to create token objects.
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	/////////////////////////////////
	// READ-WRITE & PUBLIC
	/////////////////////////////////

	// Open as read-write session but don't login.
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should be allowed to create public session objects
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv ==  CKR_USER_NOT_LOGGED_IN);

	// We should be allowed to create public token objects even when not logged in.
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv ==  CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should not be able to create private token objects because we are not logged in now
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	/////////////////////////////////
	// READ-WRITE & USER
	/////////////////////////////////

	// Open as read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login to the read-write session
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// We should always be allowed to create public session objects
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should be able allowed to create private session objects because we are logged in.
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should be allowed to create public token objects even when not logged in.
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should be able to create private token objects because we are logged in now
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	/////////////////////////////////
	// READ-WRITE & SO
	/////////////////////////////////

	// Open as read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login to the read-write session
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_SO,m_soPin1,m_soPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// We should always be allowed to create public session objects
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Only public objects can be created unless the normal user is logged in.
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);

	// We should be allowed to create public token objects even when not logged in.
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Only public objects can be created unless the normal user is logged in.
	rv = createDataObjectMinimal(hSession, ON_TOKEN, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	/////////////////////////////////
	// READ-WRITE & USER
	/////////////////////////////////

	// Open as read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login to the read-write session
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Create a secret object
	rv = CRYPTOKI_F_PTR( C_GenerateRandom(hSession, keyPtr, keyLen) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check value
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, getTemplate, 4) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(local == CK_FALSE);
	CPPUNIT_ASSERT(always == CK_FALSE);
	CPPUNIT_ASSERT(never == CK_FALSE);
	CPPUNIT_ASSERT(sizeof(allowedMechs) == getTemplate[3].ulValueLen);
	CPPUNIT_ASSERT(memcmp(&allowedMechs, &mechs, sizeof(allowedMechs)) == 0);

	// Destroy the secret object
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testCopyObject()
{
//    printf("\ntestCopyObject\n");

	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject;
	CK_OBJECT_HANDLE hObjectCopy;
	CK_OBJECT_HANDLE hObject1;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-only session and don't login
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Get a public session object
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMCD(hSession, IN_SESSION, IS_PUBLIC, CK_TRUE, CK_FALSE, CK_TRUE, hObjectCopy);
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Allowed to copy it
	const char *pLabel = "Label modified via C_CopyObject";
	CK_BBOOL bToken = CK_FALSE;
	CK_BBOOL bPrivate = CK_FALSE;
	CK_OBJECT_CLASS cClass = CKO_DATA;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_LABEL, (CK_UTF8CHAR_PTR)pLabel, strlen(pLabel) },
		{ CKA_TOKEN, &bToken, sizeof(bToken) },
		{ CKA_PRIVATE, &bPrivate, sizeof(bPrivate) },
		{ CKA_CLASS, &cClass, sizeof(cClass) }
	};
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 1, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Not allowed to copy.
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObjectCopy, &attribs[0], 1, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_ACTION_PROHIBITED);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hObjectCopy) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Still allowed when still session and public
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 3, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Not allowed to overwrite an !ck8 attribute
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 4, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_READ_ONLY);

	// Not allowed to go on token
	bToken = CK_TRUE;
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 3, &hObject1) );
	bToken = CK_FALSE;
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);

	// Not allowed to go to private
	bPrivate = CK_TRUE;
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 3, &hObject1) );
	bPrivate = CK_FALSE;
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create a read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private object
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Get a public session object
	rv = createDataObjectNormal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Allowed to go on token
	bToken = CK_TRUE;
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 3, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Allowed to go to private
	bPrivate = CK_TRUE;
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 3, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hObject1) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Not allowed to change a !ck8 parameter
	CK_BYTE id[] = "Another object ID";
	attribs[3].type = CKA_OBJECT_ID;
	attribs[3].pValue = id;
	attribs[3].ulValueLen = sizeof(id);
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 4, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_READ_ONLY);

	// Not allowed to downgrade privacy
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectNormal(hSession, IN_SESSION, IS_PRIVATE, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);
	bToken = CK_FALSE;
	bPrivate = CK_FALSE;
	rv = CRYPTOKI_F_PTR( C_CopyObject(hSession, hObject, &attribs[0], 3, &hObject1) );
	CPPUNIT_ASSERT(rv == CKR_TEMPLATE_INCONSISTENT);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testDestroyObject()
{
//    printf("\ntestDestroyObject\n");

	// [PKCS#11 v2.40, C_Logout] When logout is successful...
	// a. Any of the application's handles to private objects become invalid.
	// b. Even if a user is later logged back into the token those handles remain invalid.
	// c. All private session objects from sessions belonging to the application area destroyed.

	// [PKCS#11 v2.40, C_CreateObject]
	// Only session objects can be created during read-only session.
	// Only public objects can be created unless the normal user is logged in.

	CK_RV rv;
	CK_SESSION_HANDLE hSessionRO;
	CK_SESSION_HANDLE hSessionRW;
	CK_OBJECT_HANDLE hObjectSessionPublic;
	CK_OBJECT_HANDLE hObjectSessionPrivate;
	CK_OBJECT_HANDLE hObjectTokenPublic;
	CK_OBJECT_HANDLE hObjectTokenPrivate;
	CK_OBJECT_HANDLE hObjectDestroy;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Open read-only session on when the token is not initialized should fail
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Try to destroy an invalid object using an invalid session
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,CK_INVALID_HANDLE) );
	CPPUNIT_ASSERT(rv == CKR_SESSION_HANDLE_INVALID);

	// Create a read-only session.
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Trying to destroy an invalid object in a read-only session
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,CK_INVALID_HANDLE) );
	CPPUNIT_ASSERT(rv == CKR_OBJECT_HANDLE_INVALID);

	// Create a read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Trying to destroy an invalid object in a read-write session
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,CK_INVALID_HANDLE) );
	CPPUNIT_ASSERT(rv == CKR_OBJECT_HANDLE_INVALID);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Create all permutations of session/token, public/private objects
	rv = createDataObjectMinimal(hSessionRW, IN_SESSION, IS_PUBLIC, hObjectSessionPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, IN_SESSION, IS_PRIVATE, hObjectSessionPrivate);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, ON_TOKEN, IS_PUBLIC, hObjectTokenPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, ON_TOKEN, IS_PRIVATE, hObjectTokenPrivate);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMCD(hSessionRW, IN_SESSION, IS_PUBLIC, CK_TRUE, CK_TRUE, CK_FALSE, hObjectDestroy);
	CPPUNIT_ASSERT(rv == CKR_OK);

	// We should not be able to destroy a non-destroyable object.
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,hObjectDestroy) );
	CPPUNIT_ASSERT(rv == CKR_ACTION_PROHIBITED);

	// On a read-only session we should not be able to destroy the public token object
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,hObjectTokenPublic) );
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);

	// On a read-only session we should not be able to destroy the private token object
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,hObjectTokenPrivate) );
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);

	// Logout with a different session than the one used for login should be fine.
	rv = CRYPTOKI_F_PTR( C_Logout(hSessionRW) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Login USER into the sessions so we can destroy private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// We should be able to destroy the public session object from a read-only session.
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRO,hObjectSessionPublic) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// All private session objects should have been destroyed when logging out.
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRW,hObjectSessionPrivate) );
	CPPUNIT_ASSERT(rv == CKR_OBJECT_HANDLE_INVALID);

	// We should be able to destroy the public token object now.
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRW,hObjectTokenPublic) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// All handles to private token objects should have been invalidated when logging out.
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSessionRW,hObjectTokenPrivate) );
	CPPUNIT_ASSERT(rv == CKR_OBJECT_HANDLE_INVALID);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testGetObjectSize()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Open read-only session on when the token is not initialized should fail
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open a session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Get an object
	rv = createDataObjectMinimal(hSession, IN_SESSION, IS_PUBLIC, hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Get the object size
	CK_ULONG objectSize;
	rv = CRYPTOKI_F_PTR( C_GetObjectSize(hSession, hObject, &objectSize) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(objectSize == CK_UNAVAILABLE_INFORMATION);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testGetAttributeValue()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSessionRO;
	CK_SESSION_HANDLE hSessionRW;
	CK_OBJECT_HANDLE hObjectSessionPublic;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Open read-only session on when the token is not initialized should fail
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-only session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Try to destroy an invalid object using an invalid session
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSessionRO,CK_INVALID_HANDLE,NULL,1) );
	CPPUNIT_ASSERT(rv == CKR_ARGUMENTS_BAD);

	// Create all permutations of session/token, public/private objects
	rv = createDataObjectMinimal(hSessionRO, IN_SESSION, IS_PUBLIC, hObjectSessionPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);

	CK_OBJECT_CLASS cClass = CKO_VENDOR_DEFINED;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_CLASS, &cClass, sizeof(cClass) }
	};

	rv = CRYPTOKI_F_PTR( C_GetAttributeValue (hSessionRO,hObjectSessionPublic,&attribs[0],1) );//sizeof(attribs)/sizeof(CK_ATTRIBUTE));
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testSetAttributeValue()
{
	// [PKCS#11 v2.40, 4.1.1 Creating objects]
	//    1. If the supplied template specifies a value for an invalid attribute, then the attempt
	//    should fail with the error code CKR_ATTRIBUTE_TYPE_INVALID. An attribute
	//    is valid if it is either one of the attributes described in the Cryptoki specification or an
	//    additional vendor-specific attribute supported by the library and token.
	//
	//    2. If the supplied template specifies an invalid value for a valid attribute, then the
	//    attempt should fail with the error code CKR_ATTRIBUTE_VALUE_INVALID.
	//    The valid values for Cryptoki attributes are described in the Cryptoki specification.
	//
	//    3. If the supplied template specifies a value for a read-only attribute, then the attempt
	//    should fail with the error code CKR_ATTRIBUTE_READ_ONLY. Whether or not a
	//    given Cryptoki attribute is read-only is explicitly stated in the Cryptoki specification;
	//    however, a particular library and token may be even more restrictive than Cryptoki
	//    specifies. In other words, an attribute which Cryptoki says is not read-only may
	//    nonetheless be read-only under certain circumstances (i.e., in conjunction with some
	//    combinations of other attributes) for a particular library and token. Whether or not a
	//    given non-Cryptoki attribute is read-only is obviously outside the scope of Cryptoki.
	//
	//    4. N/A (Does not apply to C_SetAttributeValue)
	//
	//    5. If the attribute values in the supplied template, together with any default attribute
	//    values and any attribute values contributed to the object by the object-creation
	//    function itself, are inconsistent, then the attempt should fail with the error code
	//    CKR_TEMPLATE_INCONSISTENT. A set of attribute values is inconsistent if not
	//    all of its members can be satisfied simultaneously by the token, although each value
	//    individually is valid in Cryptoki. One example of an inconsistent template would be
	//    using a template which specifies two different values for the same attribute. Another
	//    example would be trying to create a secret key object with an attribute which is
	//    appropriate for various types of public keys or private keys, but not for secret keys.
	//    A final example would be a template with an attribute that violates some token
	//    specific requirement. Note that this final example of an inconsistent template is
	//    token-dependent—on a different token, such a template might not be inconsistent.
	//
	//    6. If the supplied template specifies the same value for a particular attribute more than
	//    once (or the template specifies the same value for a particular attribute that the object-
	//    creation function itself contributes to the object), then the behavior of Cryptoki is not
	//    completely specified. The attempt to create an object can either succeed—thereby
	//    creating the same object that would have been created if the multiply-specified
	//    attribute had only appeared once—or it can fail with error code
	//    CKR_TEMPLATE_INCONSISTENT. Library developers are encouraged to make
	//    their libraries behave as though the attribute had only appeared once in the template;
	//    application developers are strongly encouraged never to put a particular attribute into
	//    a particular template more than once.

	CK_RV rv;
	CK_SESSION_HANDLE hSessionRO;
	CK_SESSION_HANDLE hSessionRW;
	CK_OBJECT_HANDLE hObjectSessionPublic;
	CK_OBJECT_HANDLE hObjectSessionPrivate;
	CK_OBJECT_HANDLE hObjectTokenPublic;
	CK_OBJECT_HANDLE hObjectTokenPrivate;
	CK_OBJECT_HANDLE hObjectSet;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Open read-only session on when the token is not initialized should fail
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-only session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Create all permutations of session/token, public/private objects
	rv = createDataObjectMinimal(hSessionRO, IN_SESSION, IS_PUBLIC, hObjectSessionPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, IN_SESSION, IS_PRIVATE, hObjectSessionPrivate);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, ON_TOKEN, IS_PUBLIC, hObjectTokenPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, ON_TOKEN, IS_PRIVATE, hObjectTokenPrivate);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMCD(hSessionRO, IN_SESSION, IS_PUBLIC, CK_FALSE, CK_TRUE, CK_TRUE, hObjectSet);
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check that label can be modified on all combintations of session/token and public/private objects
	const char  *pLabel = "Label modified via C_SetAttributeValue";
	CK_ATTRIBUTE attribs[] = {
		{ CKA_LABEL, (CK_UTF8CHAR_PTR)pLabel, strlen(pLabel) }
	};

	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectSessionPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectSessionPrivate,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectTokenPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRW,hObjectTokenPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectTokenPrivate,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_SESSION_READ_ONLY);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRW,hObjectTokenPrivate,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectSet,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_ACTION_PROHIBITED);

	attribs[0].pValue = NULL_PTR;
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSessionRO,hObjectSessionPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == strlen(pLabel));

	char pStoredLabel[64];
	attribs[0].pValue = &pStoredLabel[0];
	attribs[0].ulValueLen = 64;
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSessionRO,hObjectSessionPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribs[0].ulValueLen == strlen(pLabel));
	CPPUNIT_ASSERT(memcmp(pLabel,pStoredLabel,strlen(pLabel)) == 0);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Close session
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testFindObjects()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSessionRO;
	CK_SESSION_HANDLE hSessionRW;
	CK_OBJECT_HANDLE hObjectSessionPublic;
	CK_OBJECT_HANDLE hObjectSessionPrivate;
	CK_OBJECT_HANDLE hObjectTokenPublic;
	CK_OBJECT_HANDLE hObjectTokenPrivate;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Open read-only session on when the token is not initialized should fail
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-only session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Create all permutations of session/token, public/private objects
	rv = createDataObjectMinimal(hSessionRO, IN_SESSION, IS_PUBLIC, hObjectSessionPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, IN_SESSION, IS_PRIVATE, hObjectSessionPrivate);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, ON_TOKEN, IS_PUBLIC, hObjectTokenPublic);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = createDataObjectMinimal(hSessionRW, ON_TOKEN, IS_PRIVATE, hObjectTokenPrivate);
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Set labels for the objects
	const char  *pLabel = "Label modified via C_SetAttributeValue";
	CK_ATTRIBUTE attribs[] = {
		{ CKA_LABEL, (CK_UTF8CHAR_PTR)pLabel, strlen(pLabel) }
	};
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectSessionPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRO,hObjectSessionPrivate,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRW,hObjectTokenPublic,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue (hSessionRW,hObjectTokenPrivate,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Now find the objects while logged in should find them all.
	rv = CRYPTOKI_F_PTR( C_FindObjectsInit(hSessionRO,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CK_OBJECT_HANDLE hObjects[16];
	CK_ULONG ulObjectCount = 0;
	rv = CRYPTOKI_F_PTR( C_FindObjects(hSessionRO,&hObjects[0],16,&ulObjectCount) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(4 == ulObjectCount);
	rv = CRYPTOKI_F_PTR( C_FindObjectsFinal(hSessionRO) );


	rv = CRYPTOKI_F_PTR( C_Logout(hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Now find the objects while no longer logged in should find only 2
	rv = CRYPTOKI_F_PTR( C_FindObjectsInit(hSessionRO,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_FindObjects(hSessionRO,&hObjects[0],16,&ulObjectCount) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(2 == ulObjectCount);
	rv = CRYPTOKI_F_PTR( C_FindObjectsFinal(hSessionRO) );

	// Close the session used to create the session objects, should also destroy the session objects.
	rv = CRYPTOKI_F_PTR( C_CloseSession(hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Now find just the public token object as public session object should be gone now.
	rv = CRYPTOKI_F_PTR( C_FindObjectsInit(hSessionRW,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_FindObjects(hSessionRW,&hObjects[0],16,&ulObjectCount) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(1 == ulObjectCount);
	rv = CRYPTOKI_F_PTR( C_FindObjectsFinal(hSessionRW) );

	// Login USER into the sessions so we can gain access to private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSessionRW,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Now find just the public token object as public session object should be gone now.
	rv = CRYPTOKI_F_PTR( C_FindObjectsInit(hSessionRW,&attribs[0],1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_FindObjects(hSessionRW,&hObjects[0],16,&ulObjectCount) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(2 == ulObjectCount);
	rv = CRYPTOKI_F_PTR( C_FindObjectsFinal(hSessionRW) );
}


void ObjectTests::testGenerateKeys()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSessionRO;
	CK_SESSION_HANDLE hSessionRW;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Open read-only session on when the token is not initialized should fail
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-only session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE;

	// Generate all combinations of session/token public/private key pairs.
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PUBLIC,ON_TOKEN,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PUBLIC,ON_TOKEN,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);

	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PRIVATE,IN_SESSION,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PRIVATE,IN_SESSION,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PRIVATE,ON_TOKEN,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,IN_SESSION,IS_PRIVATE,ON_TOKEN,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);

	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PUBLIC,IN_SESSION,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PUBLIC,ON_TOKEN,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PUBLIC,ON_TOKEN,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);

	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PRIVATE,IN_SESSION,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PRIVATE,IN_SESSION,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PRIVATE,ON_TOKEN,IS_PUBLIC,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = generateRsaKeyPair(hSessionRW,ON_TOKEN,IS_PRIVATE,ON_TOKEN,IS_PRIVATE,hPuk,hPrk);
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testCreateCertificates()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;

	rv = createCertificateObjectIncomplete(hSession,IN_SESSION,IS_PUBLIC,hObject);
	CPPUNIT_ASSERT(rv == CKR_TEMPLATE_INCOMPLETE);
	rv = createCertificateObjectX509(hSession,IN_SESSION,IS_PUBLIC,hObject);
	CPPUNIT_ASSERT(rv == CKR_OK);

	CK_BYTE pCheckValue[] = { 0x2b, 0x84, 0xf6 };
	CK_ATTRIBUTE attribs[] = {
		{ CKA_CHECK_VALUE, pCheckValue, sizeof(pCheckValue) }
	};

	rv = CRYPTOKI_F_PTR( C_SetAttributeValue(hSession, hObject, attribs, 1) );
	CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_READ_ONLY);
}

void ObjectTests::testDefaultDataAttributes()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;

	// Minimal data object
	CK_OBJECT_CLASS objClass = CKO_DATA;
	CK_ATTRIBUTE objTemplate[] = {
		{ CKA_CLASS, &objClass, sizeof(objClass) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create minimal data object
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check attributes in data object
	checkCommonObjectAttributes(hSession, hObject, objClass);
	checkCommonStorageObjectAttributes(hSession, hObject, CK_FALSE, CK_TRUE, CK_TRUE, NULL_PTR, 0, CK_TRUE, CK_TRUE);
	checkDataObjectAttributes(hSession, hObject, NULL_PTR, 0, NULL_PTR, 0, NULL_PTR, 0);
}

void ObjectTests::testDefaultX509CertAttributes()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;

	// Minimal X509 certificate object
	CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
	CK_CERTIFICATE_TYPE certificateType = CKC_X_509;
	CK_BYTE pSubject[] = "Test1";
	CK_BYTE pValue[] = "Test2";
	CK_BYTE pCheckValue[] = { 0x2b, 0x84, 0xf6 };
	CK_DATE emptyDate;
	CK_ATTRIBUTE objTemplate[] = {
		{ CKA_CLASS, &objClass, sizeof(objClass) },
		{ CKA_CERTIFICATE_TYPE, &certificateType, sizeof(certificateType) },
		{ CKA_SUBJECT, pSubject, sizeof(pSubject)-1 },
		{ CKA_VALUE, pValue, sizeof(pValue)-1 }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create minimal X509 certificate
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check attributes in X509 certificate object
	checkCommonObjectAttributes(hSession, hObject, objClass);
	checkCommonStorageObjectAttributes(hSession, hObject, CK_FALSE, CK_FALSE, CK_TRUE, NULL_PTR, 0, CK_TRUE, CK_TRUE);
	memset(&emptyDate, 0, sizeof(emptyDate));
	checkCommonCertificateObjectAttributes(hSession, hObject, CKC_X_509, CK_FALSE, 0, pCheckValue, sizeof(pCheckValue), emptyDate, 0, emptyDate, 0);
	checkX509CertificateObjectAttributes(hSession, hObject, pSubject, sizeof(pSubject)-1, NULL_PTR, 0, NULL_PTR, 0, NULL_PTR, 0, pValue, sizeof(pValue)-1, NULL_PTR, 0, NULL_PTR, 0, NULL_PTR, 0, 0, CKM_SHA_1);
}

void ObjectTests::testDefaultRSAPubAttributes()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;

	// Minimal RSA public key object
	CK_OBJECT_CLASS objClass = CKO_PUBLIC_KEY;
	CK_KEY_TYPE objType = CKK_RSA;
	CK_BYTE pN[] = { 0xC6, 0x47, 0xDD, 0x74, 0x3B, 0xCB, 0xDC, 0x6F, 0xCE, 0xA7,
			 0xF0, 0x5F, 0x29, 0x4B, 0x27, 0x00, 0xCC, 0x92, 0xE9, 0x20,
			 0x8A, 0x2C, 0x87, 0x36, 0x47, 0x24, 0xB0, 0xD5, 0x7D, 0xB0,
			 0x92, 0x01, 0xA0, 0xA3, 0x55, 0x2E, 0x3F, 0xFE, 0xA7, 0x4C,
			 0x4B, 0x3F, 0x9D, 0x4E, 0xCB, 0x78, 0x12, 0xA9, 0x42, 0xAD,
			 0x51, 0x1F, 0x3B, 0xBD, 0x3D, 0x6A, 0xE5, 0x38, 0xB7, 0x45,
			 0x65, 0x50, 0x30, 0x35 };
        CK_BYTE pE[] = { 0x01, 0x00, 0x01 };
	CK_DATE emptyDate;
	CK_ATTRIBUTE objTemplate[] = {
		{ CKA_CLASS, &objClass, sizeof(objClass) },
		{ CKA_KEY_TYPE, &objType, sizeof(objType) },
		{ CKA_MODULUS, pN, sizeof(pN) },
		{ CKA_PUBLIC_EXPONENT, pE, sizeof(pE) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create minimal RSA public key object
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check attributes in RSA public key object
	checkCommonObjectAttributes(hSession, hObject, objClass);
	checkCommonStorageObjectAttributes(hSession, hObject, CK_FALSE, CK_FALSE, CK_TRUE, NULL_PTR, 0, CK_TRUE, CK_TRUE);
	memset(&emptyDate, 0, sizeof(emptyDate));
	checkCommonKeyAttributes(hSession, hObject, objType, NULL_PTR, 0, emptyDate, 0, emptyDate, 0, CK_FALSE, CK_FALSE, CK_UNAVAILABLE_INFORMATION, NULL_PTR, 0);
	checkCommonPublicKeyAttributes(hSession, hObject, NULL_PTR, 0, CK_TRUE, CK_TRUE, CK_TRUE, CK_TRUE, CK_FALSE, NULL_PTR, 0);
	checkCommonRSAPublicKeyAttributes(hSession, hObject, pN, sizeof(pN), 512, pE, sizeof(pE));
}

void ObjectTests::testDefaultRSAPrivAttributes()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;

	// Minimal RSA private key object
	CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY;
	CK_KEY_TYPE objType = CKK_RSA;
	CK_BBOOL bTrue = CK_TRUE;
	CK_BBOOL bFalse = CK_FALSE;
	CK_BYTE pN[] = { 0xC6, 0x47, 0xDD, 0x74, 0x3B, 0xCB, 0xDC, 0x6F, 0xCE, 0xA7,
			 0xF0, 0x5F, 0x29, 0x4B, 0x27, 0x00, 0xCC, 0x92, 0xE9, 0x20,
			 0x8A, 0x2C, 0x87, 0x36, 0x47, 0x24, 0xB0, 0xD5, 0x7D, 0xB0,
			 0x92, 0x01, 0xA0, 0xA3, 0x55, 0x2E, 0x3F, 0xFE, 0xA7, 0x4C,
			 0x4B, 0x3F, 0x9D, 0x4E, 0xCB, 0x78, 0x12, 0xA9, 0x42, 0xAD,
			 0x51, 0x1F, 0x3B, 0xBD, 0x3D, 0x6A, 0xE5, 0x38, 0xB7, 0x45,
			 0x65, 0x50, 0x30, 0x35 };
        CK_BYTE pD[] = { 0x6D, 0x94, 0x6B, 0xEB, 0xFF, 0xDC, 0x03, 0x80, 0x7B, 0x0A,
			 0x4F, 0x0A, 0x98, 0x6C, 0xA3, 0x2A, 0x8A, 0xE4, 0xAA, 0x18,
			 0x44, 0xA4, 0xA5, 0x39, 0x37, 0x0A, 0x2C, 0xFC, 0x5F, 0xD1,
			 0x44, 0x6E, 0xCE, 0x25, 0x9B, 0xE5, 0xD1, 0x51, 0xAF, 0xA8,
			 0x30, 0xD1, 0x4D, 0x3C, 0x60, 0x33, 0xB5, 0xED, 0x4C, 0x39,
			 0xDA, 0x68, 0x78, 0xF9, 0x6B, 0x4F, 0x47, 0x55, 0xB2, 0x02,
			 0x00, 0x7E, 0x9C, 0x05 };
	CK_DATE emptyDate;
	// Make the key non-sensitive and extractable so that we can test it.
	CK_ATTRIBUTE objTemplate[] = {
		{ CKA_CLASS, &objClass, sizeof(objClass) },
		{ CKA_KEY_TYPE, &objType, sizeof(objType) },
		{ CKA_SENSITIVE, &bFalse, sizeof(bFalse) },
		{ CKA_EXTRACTABLE, &bTrue, sizeof(bTrue) },
		{ CKA_MODULUS, pN, sizeof(pN) },
		{ CKA_PRIVATE_EXPONENT, pD, sizeof(pD) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create minimal RSA public key object
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check attributes in RSA public key object
	checkCommonObjectAttributes(hSession, hObject, objClass);
	checkCommonStorageObjectAttributes(hSession, hObject, CK_FALSE, CK_TRUE, CK_TRUE, NULL_PTR, 0, CK_TRUE, CK_TRUE);
	memset(&emptyDate, 0, sizeof(emptyDate));
	checkCommonKeyAttributes(hSession, hObject, objType, NULL_PTR, 0, emptyDate, 0, emptyDate, 0, CK_FALSE, CK_FALSE, CK_UNAVAILABLE_INFORMATION, NULL_PTR, 0);
	checkCommonPrivateKeyAttributes(hSession, hObject, NULL_PTR, 0, CK_FALSE, CK_TRUE, CK_TRUE, CK_TRUE, CK_TRUE, CK_TRUE, CK_FALSE, CK_FALSE, CK_FALSE, NULL_PTR, 0, CK_FALSE);
	checkCommonRSAPrivateKeyAttributes(hSession, hObject, pN, sizeof(pN), NULL_PTR, 0, pD, sizeof(pD), NULL_PTR, 0, NULL_PTR, 0, NULL_PTR, 0, NULL_PTR, 0, NULL_PTR, 0);
}

void ObjectTests::testAlwaysNeverAttribute()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE;

	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 };
	CK_ULONG bits = 1536;
	CK_BBOOL bFalse = CK_FALSE;
	CK_BBOOL bTrue = CK_TRUE;
	CK_BBOOL always;
	CK_BBOOL never;
	CK_ATTRIBUTE pukAttribs[] = {
		{ CKA_MODULUS_BITS, &bits, sizeof(bits) }
	};
	CK_ATTRIBUTE prkAttribs[] = {
		{ CKA_SENSITIVE, &bTrue, sizeof(bTrue) },
		{ CKA_EXTRACTABLE, &bFalse, sizeof(bFalse) }
	};
	CK_ATTRIBUTE getTemplate[] = {
		{ CKA_ALWAYS_SENSITIVE, &always, sizeof(always) },
		{ CKA_NEVER_EXTRACTABLE, &never, sizeof(never) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create object
	rv = CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism, pukAttribs, 1, prkAttribs, 2, &hPuk, &hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check value
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hPrk, getTemplate, 2) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(always == CK_TRUE);
	CPPUNIT_ASSERT(never == CK_TRUE);

	// Set value
	rv = CRYPTOKI_F_PTR( C_SetAttributeValue(hSession, hPrk, prkAttribs, 2) );
	CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_READ_ONLY);

	// Create object
	prkAttribs[0].pValue = &bFalse;
	prkAttribs[1].pValue = &bTrue;
	rv = CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism, pukAttribs, 1, prkAttribs, 2, &hPuk, &hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check value
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hPrk, getTemplate, 2) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(always == CK_FALSE);
	CPPUNIT_ASSERT(never == CK_FALSE);
}

void ObjectTests::testSensitiveAttributes()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE;

	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 };
	CK_ULONG bits = 1536;
	CK_BBOOL bSensitive = CK_TRUE;
	CK_BBOOL bTrue = CK_TRUE;
	CK_ATTRIBUTE pukAttribs[] = {
		{ CKA_MODULUS_BITS, &bits, sizeof(bits) }
	};
	// Sensitive attributes cannot be revealed in plaintext even if wrapping is allowed
	CK_ATTRIBUTE prkAttribs[] = {
		{ CKA_SENSITIVE, &bSensitive, sizeof(bSensitive) },
		{ CKA_EXTRACTABLE, &bTrue, sizeof(bTrue) }
	};
	CK_ATTRIBUTE getTemplate[] = {
		{ CKA_PRIVATE_EXPONENT, NULL_PTR, 0 },
		{ CKA_PRIME_1, NULL_PTR, 0 },
		{ CKA_PRIME_2, NULL_PTR, 0 },
		{ CKA_EXPONENT_1, NULL_PTR, 0 },
		{ CKA_EXPONENT_2, NULL_PTR, 0 },
		{ CKA_COEFFICIENT, NULL_PTR, 0 }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create object
	rv = CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism, pukAttribs, 1, prkAttribs, 2, &hPuk, &hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check value
	for (int i = 0; i < 6; i++)
	{
		rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hPrk, &getTemplate[i], 1) );
		CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_SENSITIVE);
	}

	// Retry with non-sensitive object
	bSensitive = CK_FALSE;
	rv = CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism, pukAttribs, 1, prkAttribs, 2, &hPuk, &hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check value
	for (int i = 0; i < 6; i++)
	{
		rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hPrk, &getTemplate[i], 1) );
		CPPUNIT_ASSERT(rv == CKR_OK);
	}
}

void ObjectTests::testGetInvalidAttribute()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;

	// Minimal data object
	CK_OBJECT_CLASS objClass = CKO_DATA;
	CK_BBOOL bSign;
	CK_ATTRIBUTE objTemplate[] = {
		{ CKA_CLASS, &objClass, sizeof(objClass) }
	};
	CK_ATTRIBUTE getTemplate[] = {
		{ CKA_SIGN, &bSign, sizeof(bSign) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create minimal data object
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, 1, &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Check value
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, getTemplate, 1) );
	CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_TYPE_INVALID);
}

void ObjectTests::testReAuthentication()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE;

	CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 };
	CK_ULONG bits = 1024;
	CK_BBOOL bTrue = CK_TRUE;
	CK_ATTRIBUTE pukAttribs[] = {
		{ CKA_MODULUS_BITS, &bits, sizeof(bits) }
	};
	CK_ATTRIBUTE prkAttribs[] = {
		{ CKA_PRIVATE, &bTrue, sizeof(bTrue) },
		{ CKA_DECRYPT, &bTrue, sizeof(bTrue) },
		{ CKA_SIGN, &bTrue, sizeof(bTrue) },
		{ CKA_ALWAYS_AUTHENTICATE, &bTrue, sizeof(bTrue) }
	};

	CK_MECHANISM signMech = { CKM_SHA256_RSA_PKCS, NULL_PTR, 0 };
	CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
	CK_BYTE signature256[256];
	CK_ULONG signature256Len = sizeof(signature256);

	CK_MECHANISM encMech = { CKM_RSA_PKCS, NULL_PTR, 0 };
	CK_BYTE cipherText[256];
	CK_ULONG ulCipherTextLen = sizeof(cipherText);
	CK_BYTE recoveredText[256];
	CK_ULONG ulRecoveredTextLen = sizeof(recoveredText);

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create object
	rv = CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism, pukAttribs, 1, prkAttribs, 4, &hPuk, &hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Test C_Sign with re-authentication with invalid and valid PIN
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_CONTEXT_SPECIFIC,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OPERATION_NOT_INITIALIZED);
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &signMech, hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_CONTEXT_SPECIFIC,m_userPin1,m_userPin1Length-1) );
	CPPUNIT_ASSERT(rv == CKR_PIN_INCORRECT);
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_CONTEXT_SPECIFIC,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Sign(hSession, data, sizeof(data), signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Test C_Sign without re-authentication
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &signMech, hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Sign(hSession, data, sizeof(data), signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);
	rv = CRYPTOKI_F_PTR( C_Sign(hSession, data, sizeof(data), signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_OPERATION_NOT_INITIALIZED);

	// Test C_SignUpdate with re-authentication
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &signMech, hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_CONTEXT_SPECIFIC,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SignUpdate(hSession, data, sizeof(data)) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SignFinal(hSession, signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Test C_SignUpdate without re-authentication
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &signMech, hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SignUpdate(hSession, data, sizeof(data)) );
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);
	rv = CRYPTOKI_F_PTR( C_SignUpdate(hSession, data, sizeof(data)) );
	CPPUNIT_ASSERT(rv == CKR_OPERATION_NOT_INITIALIZED);

	// Test C_SignFinal with re-authentication
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &signMech, hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_CONTEXT_SPECIFIC,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SignFinal(hSession, signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Test C_SignFinal without re-authentication
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &signMech, hPrk) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_SignFinal(hSession, signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);
	rv = CRYPTOKI_F_PTR( C_SignFinal(hSession, signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_OPERATION_NOT_INITIALIZED);

	// Encrypt some data
	rv = CRYPTOKI_F_PTR( C_EncryptInit(hSession,&encMech,hPuk) );
	CPPUNIT_ASSERT(rv==CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Encrypt(hSession,data,sizeof(data),cipherText,&ulCipherTextLen) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	// Test C_Decrypt with re-authentication
	rv = CRYPTOKI_F_PTR( C_DecryptInit(hSession,&encMech,hPrk) );
	CPPUNIT_ASSERT(rv==CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_CONTEXT_SPECIFIC,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Decrypt(hSession,cipherText,ulCipherTextLen,recoveredText,&ulRecoveredTextLen) );
	CPPUNIT_ASSERT(rv==CKR_OK);
	CPPUNIT_ASSERT(memcmp(data, &recoveredText[ulRecoveredTextLen-sizeof(data)], sizeof(data)) == 0);

	// Test C_Decrypt without re-authentication
	rv = CRYPTOKI_F_PTR( C_DecryptInit(hSession,&encMech,hPrk) );
	CPPUNIT_ASSERT(rv==CKR_OK);
	rv = CRYPTOKI_F_PTR( C_Decrypt(hSession,cipherText,ulCipherTextLen,recoveredText,&ulRecoveredTextLen) );
	CPPUNIT_ASSERT(rv == CKR_USER_NOT_LOGGED_IN);
	rv = CRYPTOKI_F_PTR( C_Decrypt(hSession,cipherText,ulCipherTextLen,recoveredText,&ulRecoveredTextLen) );
	CPPUNIT_ASSERT(rv == CKR_OPERATION_NOT_INITIALIZED);
}

void ObjectTests::testAllowedMechanisms()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
	CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY;
	CK_BYTE key[65] = { "0000000000000000000000000000000000000000000000000000000000000000" };
	CK_MECHANISM_TYPE allowedMechs[] = { CKM_SHA256_HMAC, CKM_SHA512_HMAC };
	CK_ATTRIBUTE attribs[] = {
			{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
			{ CKA_CLASS, &secretClass, sizeof(secretClass) },
			{ CKA_VALUE, &key, sizeof(key)-1 },
			{ CKA_ALLOWED_MECHANISMS, &allowedMechs, sizeof(allowedMechs) }
	};

	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hKey) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };

	// SHA_1_HMAC is not an allowed mechanism
	CK_MECHANISM mechanism = { CKM_SHA_1_HMAC, NULL_PTR, 0 };
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &mechanism, hKey) );
	CPPUNIT_ASSERT(rv == CKR_MECHANISM_INVALID);

	// SHA256_HMAC is an allowed mechanism
	mechanism.mechanism = CKM_SHA256_HMAC;
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &mechanism, hKey) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CK_BYTE signature256[256];
	CK_ULONG signature256Len = sizeof(signature256);
	rv = CRYPTOKI_F_PTR( C_Sign(hSession, data, sizeof(data), signature256, &signature256Len) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// SHA384_HMAC is not an allowed mechanism
	mechanism.mechanism = CKM_SHA384_HMAC;
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &mechanism, hKey) );
	CPPUNIT_ASSERT(rv == CKR_MECHANISM_INVALID);

	// SHA512_HMAC is an allowed mechanism
	mechanism.mechanism = CKM_SHA512_HMAC;
	rv = CRYPTOKI_F_PTR( C_SignInit(hSession, &mechanism, hKey) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CK_BYTE signature512[512];
	CK_ULONG signature512Len = sizeof(signature512);
	rv = CRYPTOKI_F_PTR( C_Sign(hSession, data, sizeof(data), signature512, &signature512Len) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hKey) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

void ObjectTests::testTemplateAttribute()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;
	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;
	CK_BYTE pE[] = { 0x01, 0x00, 0x01 };
	CK_MECHANISM_TYPE allowedMechs[] = { CKM_SHA256_HMAC, CKM_SHA512_HMAC };

	// Wrap template
	CK_KEY_TYPE wrapType = CKK_SHA256_HMAC;;
	CK_ATTRIBUTE wrapTemplate[] = {
		{ CKA_KEY_TYPE, &wrapType, sizeof(wrapType) },
		{ CKA_PUBLIC_EXPONENT, pE, sizeof(pE) },
		{ CKA_ALLOWED_MECHANISMS, &allowedMechs, sizeof(allowedMechs) }
	};

	// Minimal public key object
	CK_OBJECT_CLASS objClass = CKO_PUBLIC_KEY;
	CK_KEY_TYPE objType = CKK_RSA;
	CK_BYTE pN[] = { 0xC6, 0x47, 0xDD, 0x74, 0x3B, 0xCB, 0xDC, 0x6F, 0xCE, 0xA7,
			 0xF0, 0x5F, 0x29, 0x4B, 0x27, 0x00, 0xCC, 0x92, 0xE9, 0x20,
			 0x8A, 0x2C, 0x87, 0x36, 0x47, 0x24, 0xB0, 0xD5, 0x7D, 0xB0,
			 0x92, 0x01, 0xA0, 0xA3, 0x55, 0x2E, 0x3F, 0xFE, 0xA7, 0x4C,
			 0x4B, 0x3F, 0x9D, 0x4E, 0xCB, 0x78, 0x12, 0xA9, 0x42, 0xAD,
			 0x51, 0x1F, 0x3B, 0xBD, 0x3D, 0x6A, 0xE5, 0x38, 0xB7, 0x45,
			 0x65, 0x50, 0x30, 0x35 };
	CK_ATTRIBUTE objTemplate[] = {
		{ CKA_CLASS, &objClass, sizeof(objClass) },
		{ CKA_KEY_TYPE, &objType, sizeof(objType) },
		{ CKA_MODULUS, pN, sizeof(pN) },
		{ CKA_PUBLIC_EXPONENT, pE, sizeof(pE) },
		{ CKA_WRAP_TEMPLATE, wrapTemplate, sizeof(wrapTemplate) }
	};

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession, CKU_USER, m_userPin1, m_userPin1Length) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Create minimal RSA public key object
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, objTemplate, sizeof(objTemplate)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	CK_ATTRIBUTE wrapAttribs[] = {
		{ 0, NULL_PTR, 0 },
		{ 0, NULL_PTR, 0 },
		{ 0, NULL_PTR, 0 }
	};
	CK_ATTRIBUTE wrapAttrib = { CKA_WRAP_TEMPLATE, NULL_PTR, 0 };

	// Get number of elements
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &wrapAttrib, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(wrapAttrib.ulValueLen == 3 * sizeof(CK_ATTRIBUTE));

	// Get element types and sizes
	wrapAttrib.pValue = wrapAttribs;
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &wrapAttrib, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(wrapAttrib.ulValueLen == 3 * sizeof(CK_ATTRIBUTE));
	for (size_t i = 0; i < 3; i++)
	{
		switch (wrapAttribs[i].type)
		{
			case CKA_KEY_TYPE:
				CPPUNIT_ASSERT(wrapAttribs[i].ulValueLen == sizeof(CK_KEY_TYPE));
				break;
			case CKA_PUBLIC_EXPONENT:
				CPPUNIT_ASSERT(wrapAttribs[i].ulValueLen == sizeof(pE));
				break;
			case CKA_ALLOWED_MECHANISMS:
				CPPUNIT_ASSERT(wrapAttribs[i].ulValueLen == sizeof(allowedMechs));
				break;
			default:
				CPPUNIT_ASSERT(false);
		}
	}

	// Get values
	wrapAttribs[0].pValue = (CK_VOID_PTR)malloc(wrapAttribs[0].ulValueLen);
	wrapAttribs[1].pValue = (CK_VOID_PTR)malloc(wrapAttribs[1].ulValueLen);
	wrapAttribs[2].pValue = (CK_VOID_PTR)malloc(wrapAttribs[2].ulValueLen);
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, &wrapAttrib, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	for (size_t i = 0; i < 3; i++)
	{
		switch (wrapAttribs[i].type)
		{
			case CKA_KEY_TYPE:
				CPPUNIT_ASSERT(*(CK_KEY_TYPE*) wrapAttribs[i].pValue == CKK_SHA256_HMAC);
				break;
			case CKA_PUBLIC_EXPONENT:
				CPPUNIT_ASSERT(memcmp(wrapAttribs[i].pValue, pE, sizeof(pE)) == 0);
				break;
			case CKA_ALLOWED_MECHANISMS:
				CPPUNIT_ASSERT(memcmp(wrapAttribs[i].pValue, allowedMechs, sizeof(allowedMechs)) == 0);
				break;
			default:
				CPPUNIT_ASSERT(false);
		}
	}

	free(wrapAttribs[0].pValue);
	free(wrapAttribs[1].pValue);
	free(wrapAttribs[2].pValue);
}

void ObjectTests::testCreateSecretKey()
{
	CK_RV rv;
	CK_SESSION_HANDLE hSession;

	// Just make sure that we finalize any previous tests
	CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) );

	// Initialize the library and start the test.
	rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Open read-write session
	rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSession) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	// Login USER into the sessions so we can create a private objects
	rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) );
	CPPUNIT_ASSERT(rv==CKR_OK);

	CK_BYTE genericKey[] = {
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06
	};
	CK_BYTE aesKey[] = {
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06
	};
	CK_BYTE desKey[] = {
		0x81, 0xdc, 0x9b, 0xdb, 0x52, 0xd0, 0x4d, 0xc2
	};
	CK_BYTE des2Key[] = {
		0x81, 0xdc, 0x9b, 0xdb, 0x52, 0xd0, 0x4d, 0xc2, 0x00, 0x36,
		0xdb, 0xd8, 0x31, 0x3e, 0xd0, 0x55
	};
	CK_BYTE des3Key[] = {
		0x81, 0xdc, 0x9b, 0xdb, 0x52, 0xd0, 0x4d, 0xc2, 0x00, 0x36,
		0xdb, 0xd8, 0x31, 0x3e, 0xd0, 0x55, 0xcc, 0x57, 0x76, 0xd1,
		0x6a, 0x1f, 0xb6, 0xe4
	};
	CK_BYTE genericKCV[] = { 0x5c, 0x3b, 0x7c };
	CK_BYTE aesKCV[] =     { 0x08, 0xbd, 0x28 };
	CK_BYTE desKCV[] =     { 0x08, 0xa1, 0x50 };
	CK_BYTE des2KCV[] =    { 0xa9, 0x67, 0xae };
	CK_BYTE des3KCV[] =    { 0x5c, 0x5e, 0xec };

	CK_OBJECT_HANDLE hObject = CK_INVALID_HANDLE;
	CK_BBOOL bFalse = CK_FALSE;
	CK_BBOOL bTrue = CK_TRUE;
	CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY;
	CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
	CK_ATTRIBUTE attribs[] = {
		{ CKA_VALUE, genericKey, sizeof(genericKey) },
		{ CKA_EXTRACTABLE, &bFalse, sizeof(bFalse) },
		{ CKA_CLASS, &secretClass, sizeof(secretClass) },
		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
		{ CKA_TOKEN, &bFalse, sizeof(bFalse) },
		{ CKA_PRIVATE, &bTrue, sizeof(bTrue) },
		{ CKA_SENSITIVE, &bTrue, sizeof(bTrue) }
	};

	CK_BYTE pCheckValue[3];
	CK_ATTRIBUTE attribKCV[] = {
		{ CKA_CHECK_VALUE, pCheckValue, sizeof(pCheckValue) }
	};

	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, attribKCV, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribKCV[0].ulValueLen == 3);
	CPPUNIT_ASSERT(memcmp(pCheckValue, genericKCV, 3) == 0);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	keyType = CKK_AES;
	attribs[0].pValue = aesKey;
	attribs[0].ulValueLen = sizeof(aesKey);
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, attribKCV, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribKCV[0].ulValueLen == 3);
	CPPUNIT_ASSERT(memcmp(pCheckValue, aesKCV, 3) == 0);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	keyType = CKK_DES;
	attribs[0].pValue = desKey;
	attribs[0].ulValueLen = sizeof(desKey);
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, attribKCV, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribKCV[0].ulValueLen == 3);
	CPPUNIT_ASSERT(memcmp(pCheckValue, desKCV, 3) == 0);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	keyType = CKK_DES2;
	attribs[0].pValue = des2Key;
	attribs[0].ulValueLen = sizeof(des2Key);
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, attribKCV, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribKCV[0].ulValueLen == 3);
	CPPUNIT_ASSERT(memcmp(pCheckValue, des2KCV, 3) == 0);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);

	keyType = CKK_DES3;
	attribs[0].pValue = des3Key;
	attribs[0].ulValueLen = sizeof(des3Key);
	rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hObject, attribKCV, 1) );
	CPPUNIT_ASSERT(rv == CKR_OK);
	CPPUNIT_ASSERT(attribKCV[0].ulValueLen == 3);
	CPPUNIT_ASSERT(memcmp(pCheckValue, des3KCV, 3) == 0);
	rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession,hObject) );
	CPPUNIT_ASSERT(rv == CKR_OK);
}

