Beispiel #1
0
int sc_init_library(void *f, int try_write_syslog, sc_lib *sclib,
                    Filename *pkcs11_libfile) {
    CK_FUNCTION_LIST_PTR fl  = 0;
    CK_C_GetFunctionList pGFL  = 0;
    unsigned long slot_count = 16;
    CK_SLOT_ID slots[16];
    CK_RV rv = 0;
    char *msg = "";
    sclib->hLib = LoadLibrary((char *)pkcs11_libfile);

    if (sclib->hLib == NULL) {
        msg = "sc: Cannot load PKCS 11 DLL.";
        goto err;
    }
    pGFL= (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))GetProcAddress(sclib->hLib, "C_GetFunctionList");
    if (pGFL == NULL) {
        msg = "sc: Cannot find GetFunctionList()";
        goto err;
    }
    rv = pGFL(&fl);
    if(rv != CKR_OK) {
        msg = "sc: Can't get function list";
        goto err;
    }
    rv = fl->C_Initialize (0); 
    if (CKR_OK != rv ) {
        msg = "sc: C_Initialize failed";
        goto err;
    }   
    rv = fl->C_GetSlotList (TRUE, slots, &slot_count);
    if (CKR_OK != rv) {
        msg = "sc: C_GetSlotList failed";
        goto err;
    }                       
    if (slot_count < 1) {
        msg = "sc: No token available";
        goto err;
    }
    sclib->m_fl = fl;
    return TRUE;
    
 err:
    logevent(f, msg);
    if(try_write_syslog) sc_write_syslog(msg);
    FreeLibrary(sclib->hLib);
    return FALSE;
}
Beispiel #2
0
static void
teardown_mock_module (CK_FUNCTION_LIST_PTR module)
{
	CK_RV rv;

	rv = module->C_Finalize (NULL);
	assert (rv == CKR_OK);
}
Beispiel #3
0
int EstEID_tokensChanged() {
  LOG_LOCATION;
	CK_SLOT_ID slotID;
	int changed = FALSE;
	while (fl->C_WaitForSlotEvent(CKF_DONT_BLOCK, &slotID, NULL_PTR) == CKR_OK) {
    EstEID_log("C_WaitForSlotEvent() pass cycle 1");
		changed = TRUE;
	}
	if (!changed) {
		while (fl->C_WaitForSlotEvent(CKF_DONT_BLOCK, &slotID, NULL_PTR) == CKR_OK) {
      EstEID_log("C_WaitForSlotEvent() pass cycle 2");
			changed = TRUE;
		}
	}  
  EstEID_log("tokens change %sdetected", changed ? "" : "not ");
	return changed;
}
Beispiel #4
0
int EstEID_loadCertInfo(EstEID_Certs *certs, int index) {
	CK_SLOT_ID slotID = certs->slotIDs[index];

	CK_SLOT_INFO slotInfo;
	FAIL_IF(EstEID_CK_failure("C_GetSlotInfo", fl->C_GetSlotInfo(slotID, &slotInfo)));

	if (!(slotInfo.flags & CKF_TOKEN_PRESENT)) return SUCCESS;

	CK_TOKEN_INFO tokenInfo;
	FAIL_IF(EstEID_CK_failure("C_GetTokenInfo", fl->C_GetTokenInfo(slotID, &tokenInfo)));

	certs->certs[index] = EstEID_createCertMap(tokenInfo);

	FAIL_UNLESS(EstEID_loadCertInfoEntries(certs, index));

	return SUCCESS;
}
Beispiel #5
0
int EstEID_getRemainingTries(CK_SLOT_ID slotID) {
	CK_TOKEN_INFO tokenInfo;
	if (EstEID_CK_failure("C_GetTokenInfo", fl->C_GetTokenInfo(slotID, &tokenInfo))) return -1;
	EstEID_log("flags: %li (%lx)", tokenInfo.flags, tokenInfo.flags);
	if (tokenInfo.flags & CKF_USER_PIN_LOCKED) return 0;
	else if (tokenInfo.flags & CKF_USER_PIN_FINAL_TRY) return 1;
	else if (tokenInfo.flags & CKF_USER_PIN_COUNT_LOW) return 2;
	else return 3;
}
Beispiel #6
0
static void
test_initialize_finalize (void)
{
	CK_FUNCTION_LIST_PTR proxy;
	CK_RV rv;

	rv = C_GetFunctionList (&proxy);
	assert (rv == CKR_OK);

	assert (p11_proxy_module_check (proxy));

	rv = proxy->C_Initialize (NULL);
	assert (rv == CKR_OK);

	rv = proxy->C_Finalize (NULL);
	assert_num_eq (rv, CKR_OK);

	p11_proxy_module_cleanup ();
}
int testStability_sign
(
	CK_SESSION_HANDLE hSession,
	CK_OBJECT_HANDLE hPrivateKey,
	CK_BYTE_PTR pData,
	CK_ULONG ulDataLen,
	CK_BYTE_PTR *ppSignature,
	CK_ULONG_PTR pulSignatureLen
)
{
	CK_RV rv;
	CK_MECHANISM mechanism = {
		CKM_RSA_PKCS, NULL_PTR, 0
	};

	rv = p11->C_SignInit(hSession, &mechanism, hPrivateKey);
	if (rv != CKR_OK)
	{
		printf("ERROR: Failed to initialize signing. rv=%s\n", rv2string(rv));
		return 1;
	}

	*pulSignatureLen = 0;
	rv = p11->C_Sign(hSession, pData, ulDataLen, NULL_PTR, pulSignatureLen);
	if (rv != CKR_OK)
	{
		printf("ERROR: Failed to check the size of the signature. rv=%s\n", rv2string(rv));
		return 1;
	}
	*ppSignature = (CK_BYTE_PTR)malloc(*pulSignatureLen);

	rv = p11->C_Sign(hSession, pData, ulDataLen, *ppSignature, pulSignatureLen);
	if (rv != CKR_OK)
	{
		printf("ERROR: Failed to sign the data. rv=%s\n", rv2string(rv));
		free(*ppSignature);
		*ppSignature = NULL;
		*pulSignatureLen = 0;
		return 1;
	}

	return 0;
}
// Connect and login to the token
int openP11(CK_SLOT_ID slotID, char* userPIN, CK_SESSION_HANDLE* hSession)
{
	char user_pin_copy[MAX_PIN_LEN+1];
	CK_RV rv;

	rv = p11->C_OpenSession(slotID, CKF_SERIAL_SESSION | CKF_RW_SESSION,
					NULL_PTR, NULL_PTR, hSession);
	if (rv != CKR_OK)
	{
		if (rv == CKR_SLOT_ID_INVALID)
		{
			fprintf(stderr, "ERROR: The given slot does not exist.\n");
		}
		else
		{
			fprintf(stderr, "ERROR: Could not open a session on the given slot.\n");
		}
		return 1;
	}

	// Get the password
	if (getPW(userPIN, user_pin_copy, CKU_USER) != 0)
	{
		fprintf(stderr, "ERROR: Could not get user PIN\n");
		return 1;
	}

	rv = p11->C_Login(*hSession, CKU_USER, (CK_UTF8CHAR_PTR)user_pin_copy, strlen(user_pin_copy));
	if (rv != CKR_OK)
	{
		if (rv == CKR_PIN_INCORRECT) {
			fprintf(stderr, "ERROR: The given user PIN does not match the one in the token.\n");
		}
		else
		{
			fprintf(stderr, "ERROR: Could not log in on the token.\n");
		}
		return 1;
	}

	return 0;
}
// Extract the information about the public RSA key and save it in the token
int dbRSAPub2session(sqlite3* /*db*/, CK_OBJECT_HANDLE objectID, CK_SESSION_HANDLE hSession)
{
	int result = 0;
	int i;
	CK_OBJECT_HANDLE hKey;
	CK_RV rv;

	CK_ATTRIBUTE pubTemplate[] = {
		{ CKA_CLASS,		NULL,	0 },
		{ CKA_KEY_TYPE,		NULL,	0 },
		{ CKA_TOKEN,		NULL,	0 },
		{ CKA_PRIVATE,		NULL,	0 },
		{ CKA_MODIFIABLE,	NULL,	0 },
		{ CKA_LABEL,		NULL,	0 },
		{ CKA_ID,		NULL,	0 },
		{ CKA_START_DATE,	NULL,	0 },
		{ CKA_END_DATE,		NULL,	0 },
		{ CKA_DERIVE,		NULL,	0 },
		{ CKA_SUBJECT,		NULL,	0 },
		{ CKA_ENCRYPT,		NULL,	0 },
		{ CKA_VERIFY,		NULL,	0 },
		{ CKA_VERIFY_RECOVER,	NULL,	0 },
		{ CKA_WRAP,		NULL,	0 },
		{ CKA_MODULUS,		NULL,	0 },
		{ CKA_PUBLIC_EXPONENT,	NULL,	0 }
	};

	for (i = 0; i < 17; i++)
	{
		result = getAttribute(objectID, &pubTemplate[i]);
		if (result)
		{
			freeTemplate(pubTemplate, 17);
			return 1;
		}
	}

	rv = p11->C_CreateObject(hSession, pubTemplate, 17, &hKey);
	if (rv != CKR_OK)
	{
		fprintf(stderr, "ERROR %X: Could not save the public key in the token. "
				"Skipping object %lu\n", (unsigned int)rv, objectID);
		result = 1;
	}
	else
	{
		printf("Object %lu has been migrated\n", objectID);
	}

	freeTemplate(pubTemplate, 17);

	return result;
}
Beispiel #10
0
static void
test_initialize_multiple (void)
{
	CK_FUNCTION_LIST_PTR proxy;
	CK_RV rv;

	rv = C_GetFunctionList (&proxy);
	assert (rv == CKR_OK);

	assert (p11_proxy_module_check (proxy));

	rv = proxy->C_Initialize (NULL);
	assert (rv == CKR_OK);

	rv = proxy->C_Initialize (NULL);
	assert (rv == CKR_OK);

	rv = proxy->C_Finalize (NULL);
	assert (rv == CKR_OK);

	rv = proxy->C_Finalize (NULL);
	assert (rv == CKR_OK);

	rv = proxy->C_Finalize (NULL);
	assert (rv == CKR_CRYPTOKI_NOT_INITIALIZED);

	p11_proxy_module_cleanup ();
}
Beispiel #11
0
CK_RV pkcs11_find_object(CK_FUNCTION_LIST_PTR funcs, FILE *out,
                         CK_SESSION_HANDLE h_session,
                         CK_ATTRIBUTE_PTR search, CK_ULONG length,
                         CK_OBJECT_HANDLE_PTR objects,
                         CK_ULONG count, CK_ULONG_PTR found)
{
    CK_ULONG f;
    CK_RV rc;

    rc = funcs->C_FindObjectsInit(h_session, search, length);
    if (rc != CKR_OK) {
        if(out) {
            show_error(out, "C_FindObjectsInit", rc);
        }
        return rc;
    }

    rc = funcs->C_FindObjects(h_session, objects, count, &f);
    if (rc != CKR_OK) {
        if(out) {
            show_error(out, "C_FindObjects", rc);
        }
        return rc;
    }

    rc = funcs->C_FindObjectsFinal(h_session);
    if (rc != CKR_OK) {
        if(out) {
            show_error(out, "C_FindObjectsFinal", rc);
        }
        return rc;
    }

    if(found) {
        *found = f;
    }

    return rc;
}
Beispiel #12
0
CK_RV pkcs11_close(FILE *err, CK_FUNCTION_LIST_PTR funcs, CK_SESSION_HANDLE h_session)
{
    CK_RV rc = funcs->C_Logout(h_session);
    if (rc != CKR_OK) {
        show_error(err, "C_Logout", rc);
        return rc;
    }

    rc = funcs->C_CloseSession(h_session);
    if (rc != CKR_OK) {
        show_error(err, "C_CloseSession", rc);
        return rc;
    }

    rc = funcs->C_Finalize(NULL);
    if (rc != CKR_OK) {
        show_error(err, "C_Finalize", rc);
        return rc;
    }

    return rc;
}
static void test_token_info() {

  const CK_CHAR_PTR TOKEN_LABEL  = "YubiKey PIV";
  const CK_CHAR_PTR TOKEN_MODEL  = "YubiKey ";  // Skip last 3 characters (version dependent)
  const CK_CHAR_PTR TOKEN_SERIAL = "1234";
  const CK_FLAGS TOKEN_FLAGS = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED;
  const CK_VERSION HW = {0, 0};
  const CK_CHAR_PTR TOKEN_TIME   = "                ";
  CK_TOKEN_INFO info;

  asrt(funcs->C_Initialize(NULL), CKR_OK, "INITIALIZE");

  asrt(funcs->C_GetTokenInfo(0, &info), CKR_OK, "GetTokeninfo");
  asrt(strncmp(info.label, TOKEN_LABEL, strlen(TOKEN_LABEL)), 0, "TOKEN_LABEL");
  // Skip manufacturer id (not used)
  asrt(strncmp(info.model, TOKEN_MODEL, strlen(TOKEN_MODEL)), 0, "TOKEN_MODEL");
  asrt(strncmp(info.serialNumber, TOKEN_SERIAL, strlen(TOKEN_SERIAL)), 0, "SERIAL_NUMBER");
  asrt(info.flags, TOKEN_FLAGS, "TOKEN_FLAGS");
  asrt(info.ulMaxSessionCount, CK_UNAVAILABLE_INFORMATION, "MAX_SESSION_COUNT");
  asrt(info.ulSessionCount, CK_UNAVAILABLE_INFORMATION, "SESSION_COUNT");
  asrt(info.ulMaxRwSessionCount, CK_UNAVAILABLE_INFORMATION, "MAX_RW_SESSION_COUNT");
  asrt(info.ulRwSessionCount, CK_UNAVAILABLE_INFORMATION, "RW_SESSION_COUNT");
  asrt(info.ulMaxPinLen, 8, "MAX_PIN_LEN");
  asrt(info.ulMinPinLen, 6, "MIN_PIN_LEN");
  asrt(info.ulTotalPublicMemory, CK_UNAVAILABLE_INFORMATION, "TOTAL_PUB_MEM");
  asrt(info.ulFreePublicMemory, CK_UNAVAILABLE_INFORMATION, "FREE_PUB_MEM");
  asrt(info.ulTotalPrivateMemory, CK_UNAVAILABLE_INFORMATION, "TOTAL_PVT_MEM");
  asrt(info.ulFreePrivateMemory, CK_UNAVAILABLE_INFORMATION, "FREE_PVT_MEM");
  asrt(info.hardwareVersion.major, HW.major, "HW_MAJ");
  asrt(info.hardwareVersion.minor, HW.minor, "HW_MIN");

  if (info.firmwareVersion.major != 4 && info.firmwareVersion.major != 0)
    asrt(info.firmwareVersion.major, 4, "FW_MAJ");

  asrt(strcmp(info.utcTime, TOKEN_TIME), 0, "TOKEN_TIME");

  asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE");

}
Beispiel #14
0
static void lib_info() {

  CK_INFO info;
  CK_FUNCTION_LIST_PTR funcs;

  if (C_GetFunctionList(&funcs) != CKR_OK) {
    fprintf(stderr, "Get function list failed\n");
    exit(EXIT_FAILURE);
  }

  if (funcs->C_GetInfo(&info) != CKR_OK) {
    fprintf(stderr, "GetInfo failed\n");
    exit(EXIT_FAILURE);
  }

  if (strcmp(info.manufacturerID, MANUFACTURER_ID) != 0) {
    fprintf(stderr, "unexpected manufacturer ID %s\n", info.manufacturerID);
    exit(EXIT_FAILURE);
  }

  if (info.cryptokiVersion.major != CRYPTOKI_VERSION_MAJ ||
      info.cryptokiVersion.minor != CRYPTOKI_VERSION_MIN ) {
    fprintf(stderr, "unexpected Cryptoki version %d.%d\n", info.cryptokiVersion.major, info.cryptokiVersion.minor);
    exit(EXIT_FAILURE);
  }

  if (info.libraryVersion.major != YKCS11_VERSION_MAJOR ||
      info.libraryVersion.minor != ((YKCS11_VERSION_MINOR * 100) + YKCS11_VERSION_PATCH )) {
    fprintf(stderr, "unexpected YKCS11 version %d.%d\n", info.libraryVersion.major, info.libraryVersion.minor);
    exit(EXIT_FAILURE);
  }

  if (strcmp(info.libraryDescription, YKCS11_DESCRIPTION) != 0) {
    fprintf(stderr, "unexpected description %s\n", info.libraryDescription);
    exit(EXIT_FAILURE);
  }

}
Beispiel #15
0
CK_RV login(CK_FUNCTION_LIST_PTR p11p, CK_SESSION_HANDLE hSession, int admin, CK_UTF8CHAR *password, CK_ULONG passwordLen) {
    CK_UTF8CHAR pin[64];
    CK_ULONG pinLen = sizeof(pin) - 1;
    CK_RV rv;
    
    if (passwordLen > 0 && password != NULL && passwordLen <= pinLen) {
        memcpy(pin, password, passwordLen);
        pinLen = passwordLen;
    } else {
        printf("Enter %sPIN: ", (admin == 1) ? "admin " : "");
        rv = getPassword(pin, &pinLen);
        if (rv!= 0)
            return(-1);
    }
    
    if (admin == 1)
        rv = p11p->C_Login(hSession, CKU_SO, pin, pinLen);
    else
        rv = p11p->C_Login(hSession, CKU_USER, pin, pinLen);
    
    memset(pin, 0, sizeof(pin));
    return(rv);
}
Beispiel #16
0
THREAD_RETURN_TYPE EstEID_initializeCryptokiThread(void *v) {
	LOG_LOCATION;	
#ifdef _WIN32
	WaitForSingleObject(initialization_mutex, INFINITE);
#else
	pthread_mutex_lock(&initialization_mutex);
#endif	
	CK_C_INITIALIZE_ARGS  init_args;
	memset( &init_args, 0x0, sizeof(init_args) );
	init_args.flags = CKF_OS_LOCKING_OK;

	struct timeval start = EstEID_startTimer();
	initialization_result = fl->C_Initialize(&init_args);
	EstEID_log("C_Initialize: %s (%li)", pkcs11_error_message(initialization_result), initialization_result);
	EstEID_stopTimerAndLog(start, "C_Initialize:");

	if (initialization_result == CKR_CRYPTOKI_ALREADY_INITIALIZED) {
		EstEID_log("initialization_result == CKR_CRYPTOKI_ALREADY_INITIALIZED");
	}

	if (initialization_result == CKR_OK) {
		CK_INFO info;
		EstEID_log("initialization_result == CKR_OK");
		if (!EstEID_CK_failure("C_GetInfo", fl->C_GetInfo(&info))) {
			EstEID_logInitInfo(info);
		};
	}
	initialization_completed = TRUE;
#ifdef _WIN32
	ReleaseMutex(initialization_mutex);
	return TRUE;
#else
	pthread_cond_broadcast(&initialization_condition);
	pthread_mutex_unlock(&initialization_mutex);
	pthread_exit(NULL);
#endif
}
Beispiel #17
0
static CK_FUNCTION_LIST_PTR
setup_mock_module (CK_SESSION_HANDLE *session)
{
	CK_FUNCTION_LIST_PTR proxy;
	CK_SLOT_ID slots[32];
	CK_RV rv;

	rv = C_GetFunctionList (&proxy);
	assert (rv == CKR_OK);

	assert (p11_proxy_module_check (proxy));

	rv = proxy->C_Initialize (NULL);
	assert (rv == CKR_OK);

	mock_slots_all = 32;
	rv = proxy->C_GetSlotList (CK_FALSE, slots, &mock_slots_all);
	assert (rv == CKR_OK);
	assert (mock_slots_all >= 2);

	/* Assume this is the slot we want to deal with */
	mock_slot_one_id = slots[0];
	mock_slot_two_id = slots[1];

	rv = proxy->C_GetSlotList (CK_TRUE, NULL, &mock_slots_present);
	assert (rv == CKR_OK);
	assert (mock_slots_present > 1);

	if (session) {
		rv = (proxy->C_OpenSession) (mock_slot_one_id,
		                             CKF_RW_SESSION | CKF_SERIAL_SESSION,
		                             NULL, NULL, session);
		assert (rv == CKR_OK);
	}

	return proxy;
}
Beispiel #18
0
int EstEID_sighHashWindows(char **signature, unsigned int *signatureLength, CK_SLOT_ID slotID, EstEID_Map cert, const char *hash, unsigned int hashLength, EstEID_PINPromptData pinPromptData) {
	CK_SESSION_HANDLE session = 0L;
	LOG_LOCATION;

	if (EstEID_CK_failure("C_OpenSession", fl->C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session))) return FAILURE;

	char message[1024];

	int remainingTries = EstEID_getRemainingTries(slotID);
	EstEID_log("EstEID_getRemainingTries(slotID) = %i", remainingTries);
	if (remainingTries == -1)
		CLOSE_SESSION_AND_RETURN(FAILURE);
	if (!remainingTries) {
		sprintf(EstEID_error, "C_Login error: %s (%li)", pkcs11_error_message(CKR_PIN_LOCKED), CKR_PIN_LOCKED);
		CLOSE_SESSION_AND_RETURN(FAILURE);
	}
	if (remainingTries < 3) {
		sprintf(message, "%s %i", l10n("Tries left:"), remainingTries);
	}
	else {
		message[0] = 0;
	}

	CK_RV loginResult = CKR_FUNCTION_CANCELED;
	
	LOG_LOCATION;
	EstEID_log("(unsigned char *)pinPromptData.pin2 = %s", (unsigned char *)pinPromptData.pin2);
	loginResult = fl->C_Login(session, CKU_USER, (unsigned char *)pinPromptData.pin2, strlen(pinPromptData.pin2));
	if(loginResult != CKR_OK) {
		EstEID_log("loginResult = %s", pkcs11_error_message(loginResult));
		sprintf(EstEID_error, "C_Login error: %s (%li)", pkcs11_error_message(loginResult), loginResult);
		CLOSE_SESSION_AND_RETURN(loginResult);
	}
	
	return EstEID_RealSign(session, signature, signatureLength, hash, hashLength, NULL);
}
Beispiel #19
0
int EstEID_RealSign(CK_SESSION_HANDLE session, char **signature, unsigned int *signatureLength, const char *hash, unsigned int hashLength, char* name) {
	CK_OBJECT_HANDLE privateKeyHandle;
	CK_ULONG objectCount;
	unsigned int hashWithPaddingLength = 0;
	char *hashWithPadding;
	CK_MECHANISM mechanism = {CKM_RSA_PKCS, 0, 0};
	CK_OBJECT_CLASS objectClass = CKO_PRIVATE_KEY;
	CK_ATTRIBUTE searchAttribute = {CKA_CLASS, &objectClass, sizeof(objectClass)};

	if (EstEID_CK_failure("C_FindObjectsInit", fl->C_FindObjectsInit(session, &searchAttribute, 1))) CLOSE_SESSION_AND_RETURN(FAILURE);

	if (EstEID_CK_failure("C_FindObjects", fl->C_FindObjects(session, &privateKeyHandle, 1, &objectCount))) CLOSE_SESSION_AND_RETURN(FAILURE);
	if (EstEID_CK_failure("C_FindObjectsFinal", fl->C_FindObjectsFinal(session))) CLOSE_SESSION_AND_RETURN(FAILURE);

	if (objectCount == 0) CLOSE_SESSION_AND_RETURN(FAILURE); // todo ?? set error message

	if (EstEID_CK_failure("C_SignInit", fl->C_SignInit(session, &mechanism, privateKeyHandle))) CLOSE_SESSION_AND_RETURN(FAILURE);

	hashWithPadding = EstEID_addPadding(hash, hashLength, &hashWithPaddingLength);
	if (hashWithPadding) { // This is additional safeguard, as digest length is checked already before calling EstEID_addPadding()
		CK_ULONG len;
		if (EstEID_CK_failure("C_Sign", fl->C_Sign(session, (CK_BYTE_PTR)hashWithPadding, hashWithPaddingLength, NULL, &len))) {
			free(hashWithPadding);
			CLOSE_SESSION_AND_RETURN(FAILURE);
		}
		*signature = (char *)malloc(len);
		if (EstEID_CK_failure("C_Sign", fl->C_Sign(session, (CK_BYTE_PTR)hashWithPadding, hashWithPaddingLength, (CK_BYTE_PTR) * signature, &len))) {
			free(hashWithPadding);
			CLOSE_SESSION_AND_RETURN(FAILURE);
		}
		*signatureLength = len;
		free(hashWithPadding);		
	}

	if (session) {
		if (EstEID_CK_failure("C_CloseSession", fl->C_CloseSession(session))) {
			return FAILURE;
		}
	}

	if(name) {
		free(name);
	}
  
	if (!hashWithPaddingLength) { // This is additional safeguard, as digest length is checked already before calling EstEID_addPadding()
		EstEID_log("will not sign due to incorrect incoming message digest length");
		return FAILURE;
	}
	EstEID_log("successfully signed");
	return SUCCESS;
}
static void test_session() {

  CK_SESSION_HANDLE session;
  CK_SESSION_INFO   info;

  asrt(funcs->C_Initialize(NULL), CKR_OK, "INITIALIZE");

  asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1");
  asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession");

  asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession2");
  asrt(funcs->C_GetSessionInfo(session, &info), CKR_OK, "GetSessionInfo");
  asrt(info.state, CKS_RW_PUBLIC_SESSION, "CHECK STATE");
  asrt(info.flags, CKF_SERIAL_SESSION | CKF_RW_SESSION, "CHECK FLAGS");
  asrt(info.ulDeviceError, 0, "CHECK DEVICE ERROR");
  asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession");

  asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession3");
  asrt(funcs->C_CloseAllSessions(0), CKR_OK, "CloseAllSessions");

  asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE");

}
Beispiel #21
0
/*
 * Log in to the keystore in the child if we were logged in in the parent. There
 * are similarities in the code with pk11_token_login() but still it is quite
 * different so we need a separate function for this.
 *
 * Note that this function is called under the locked session mutex when fork is
 * detected. That means that C_Login() will be called from the child just once.
 *
 * Returns:
 *	1 on success
 *	0 on failure
 */
int
pk11_token_relogin(CK_SESSION_HANDLE session)
	{
	CK_RV rv;

	/*
	 * We are in the child so check if we should login to the token again.
	 * Note that it is enough to log in to the token through one session
	 * only, all already open and all future sessions can access the token
	 * then.
	 */
	if (passphrasedialog != NULL)
		{
		char *pin = NULL;

		/* If we cached the PIN then use it. */
		if (token_pin != NULL)
			pin = token_pin;
		else if (pk11_get_pin(passphrasedialog, &pin) == 0)
			goto err;

		(void) pthread_mutex_lock(uri_lock);
		if ((rv = pFuncList->C_Login(session, CKU_USER,
		    (CK_UTF8CHAR_PTR)pin, strlen(pin))) != CKR_OK)
			{
			PK11err_add_data(PK11_F_TOKEN_RELOGIN,
			    PK11_R_TOKEN_LOGIN_FAILED, rv);
			(void) pthread_mutex_unlock(uri_lock);
			goto err;
			}
		(void) pthread_mutex_unlock(uri_lock);

		/* Forget the PIN now if we did not cache it before. */
		if (pin != token_pin)
			{
			memset(pin, 0, strlen(pin));
			OPENSSL_free(pin);
			}
		}

	return (1);
err:
	return (0);
	}
static void test_login() {

  CK_SESSION_HANDLE session;
  CK_SESSION_INFO   info;

  asrt(funcs->C_Initialize(NULL), CKR_OK, "INITIALIZE");

  asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1");

  asrt(funcs->C_Login(session, CKU_USER, "123456", 6), CKR_OK, "Login USER");
  asrt(funcs->C_Logout(session), CKR_OK, "Logout USER");

  asrt(funcs->C_Login(session, CKU_SO, "010203040506070801020304050607080102030405060708", 48), CKR_OK, "Login SO");
  asrt(funcs->C_Logout(session), CKR_OK, "Logout SO");

  asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession");

  asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE");

}
static void test_lib_info() {

  const CK_CHAR_PTR MANUFACTURER_ID    = "Yubico (www.yubico.com)";
  const CK_CHAR_PTR YKCS11_DESCRIPTION = "PKCS#11 PIV Library (SP-800-73)";
  const CK_ULONG CRYPTOKI_VERSION_MAJ  = 2;
  const CK_ULONG CRYPTOKI_VERSION_MIN  = 40;


  CK_INFO info;

  asrt(funcs->C_GetInfo(&info), CKR_OK, "GET_INFO");

  asrt(strcmp(info.manufacturerID, MANUFACTURER_ID), 0, "MANUFACTURER");

  asrt(info.cryptokiVersion.major, CRYPTOKI_VERSION_MAJ, "CK_MAJ");
  asrt(info.cryptokiVersion.minor, CRYPTOKI_VERSION_MIN, "CK_MIN");

  asrt(info.libraryVersion.major, YKCS11_VERSION_MAJOR, "LIB_MAJ");
  asrt(info.libraryVersion.minor, ((YKCS11_VERSION_MINOR * 10) + YKCS11_VERSION_PATCH ), "LIB_MIN");

  asrt(strcmp(info.libraryDescription, YKCS11_DESCRIPTION), 0, "LIB_DESC");
}
Beispiel #24
0
int testStability_generate(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE *hPublicKey, CK_OBJECT_HANDLE *hPrivateKey)
{
	CK_RV rv;
	CK_BBOOL ckTrue = CK_TRUE;
	CK_MECHANISM keyGenMechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0};
	CK_BYTE publicExponent[] = { 1, 0, 1 };
	CK_ULONG modulusBits = 1024;
	CK_MECHANISM mechanism = {
		CKM_VENDOR_DEFINED, NULL_PTR, 0
	};

	CK_ATTRIBUTE publicKeyTemplate[] = {
		{ CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) },
		{ CKA_VERIFY, &ckTrue, sizeof(ckTrue) },
		{ CKA_WRAP, &ckTrue, sizeof(ckTrue) },
		{ CKA_TOKEN, &ckTrue, sizeof(ckTrue) },
		{ CKA_MODULUS_BITS, &modulusBits, sizeof(modulusBits) },
		{ CKA_PUBLIC_EXPONENT, &publicExponent, sizeof(publicExponent) }
	};
	CK_ATTRIBUTE privateKeyTemplate[] = {
		{ CKA_PRIVATE, &ckTrue, sizeof(ckTrue) },
		{ CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) },
		{ CKA_DECRYPT, &ckTrue, sizeof(ckTrue) },
		{ CKA_SIGN, &ckTrue, sizeof(ckTrue) },
		{ CKA_UNWRAP, &ckTrue, sizeof(ckTrue) },
		{ CKA_TOKEN, &ckTrue, sizeof(ckTrue) }
	};

	printf("Generating a key pair...\n");
	rv = p11->C_GenerateKeyPair(hSession, &keyGenMechanism, publicKeyTemplate, 6, privateKeyTemplate, 6, hPublicKey, hPrivateKey);
	if (rv != CKR_OK)
	{
		printf("ERROR: Failed to generate a keypair. rv=%s\n", rv2string(rv));
		return 1;
	}

	return 0;
}
Beispiel #25
0
THREAD_RETURN_TYPE EstEID_pinPadLogin(void* threadData) {
	LOG_LOCATION;
#ifndef _WIN32
	pthread_mutex_lock(&pinpad_thread_mutex);
	CK_SESSION_HANDLE session = ((EstEID_PINPadThreadData*)threadData)->session;
	CK_RV loginResult = fl->C_Login(session, CKU_USER, NULL, 0);		
	((EstEID_PINPadThreadData*)threadData)->result = loginResult;

	closePinPadModalSheet();
	EstEID_log("modal sheet/dialog destroyed");

	pinpad_thread_completed = TRUE;
	pthread_cond_broadcast(&pinpad_thread_condition);
	pthread_mutex_unlock(&pinpad_thread_mutex);
	pthread_exit(NULL);
#else
	WaitForSingleObject(pinpad_thread_mutex, INFINITE);
	EstEID_PINPromptDataEx* pinPromptDataEx = (EstEID_PINPromptDataEx*)threadData;
	pinPromptDataEx->pinPromptData.promptFunction(NULL, pinPromptDataEx->name, pinPromptDataEx->message, 0, true);	
	ReleaseMutex(pinpad_thread_mutex);
	return TRUE;
#endif
}
static CK_RV
hacky_perform_initialize_pin (GP11Slot *slot)
{
	CK_FUNCTION_LIST_PTR funcs;
	CK_SESSION_HANDLE session;
	CK_SLOT_ID slot_id;
	CK_RV rv;
	
	/* 
	 * This hack only works when:
	 *  
	 *  - Module is protected authentication path
	 *  - No other sessions are open.
	 *  
	 *  Thankfully this is the case with mate-keyring-daemon and 
	 *  the mate-keyring tool. 
	 */
	
	funcs = gp11_module_get_functions (gp11_slot_get_module (slot));
	g_return_val_if_fail (funcs, CKR_GENERAL_ERROR);
	slot_id = gp11_slot_get_handle (slot);
	
	rv = funcs->C_OpenSession (slot_id, CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL, NULL, &session);
	if (rv != CKR_OK)
		return rv;
	
	rv = funcs->C_Login (session, CKU_SO, NULL, 0);
	if (rv == CKR_OK) {
		rv = funcs->C_InitPIN (session, NULL, 0);
		funcs->C_Logout (session);
	}
	
	funcs->C_CloseSession (session);
	
	return rv;
}
Beispiel #27
0
int EstEID_signHash(char **signature, unsigned int *signatureLength, CK_SLOT_ID slotID, EstEID_Map cert, const char *hash, unsigned int hashLength, EstEID_PINPromptData pinPromptData) {
	CK_SESSION_HANDLE session = 0L;
	LOG_LOCATION;

	if (EstEID_CK_failure("C_OpenSession", fl->C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session))) return FAILURE;

	char *name = EstEID_getFullNameWithPersonalCode(cert);

	for (int attempt = 0, blocked = FALSE;; attempt++) {
		char message[1024];

		int remainingTries = EstEID_getRemainingTries(slotID);
		if (remainingTries == -1)
			CLOSE_SESSION_AND_RETURN(FAILURE);
		if (!remainingTries || blocked) {
			sprintf(EstEID_error, "C_Login error: %s (%li)", pkcs11_error_message(CKR_PIN_LOCKED), CKR_PIN_LOCKED);
			pinPromptData.alertFunction(pinPromptData.nativeWindowHandle, l10n("PIN2 blocked, can not sign!"));
			CLOSE_SESSION_AND_RETURN(FAILURE);
		}
		if (remainingTries < 3 || attempt) {
			sprintf(message, "%s%s %i", (attempt ? l10n("Incorrect PIN2! ") : ""), l10n("Tries left:"), remainingTries);
		}
		else {
			message[0] = 0;
		}

		int isPinPad = EstEID_isPinPad(slotID);
		CK_RV loginResult = CKR_FUNCTION_CANCELED;
		if(!isPinPad) {
			// Simple card reader
			char *pin = pinPromptData.promptFunction(pinPromptData.nativeWindowHandle, name, message, (unsigned)atoi(EstEID_mapGet(cert, "minPinLen")), isPinPad);
			if (!pin || strlen(pin) == 0) {
				if (pin) free(pin);
				setUserCancelErrorCodeAndMessage();
				CLOSE_SESSION_AND_RETURN(FAILURE);
			}
			loginResult = fl->C_Login(session, CKU_USER, (unsigned char *)pin, strlen(pin));
			free(pin);
		}
		else {
			// PIN pad			
#ifdef _WIN32
			EstEID_log("creating pinpad dialog UI thread");
			pinpad_thread_result = -1;
			FAIL_IF_THREAD_ERROR("CreateMutex", (pinpad_thread_mutex = CreateMutex(NULL, FALSE, NULL)));
#else
			EstEID_log("creating pinpad worker thread");
			pinpad_thread_result = -1;
			FAIL_IF_PTHREAD_ERROR("pthread_mutex_init", pthread_mutex_init(&pinpad_thread_mutex, NULL));
			FAIL_IF_PTHREAD_ERROR("pthread_cond_init", pthread_cond_init(&pinpad_thread_condition, NULL));
			pthread_t pinpad_thread;
			EstEID_PINPadThreadData threadData;
			threadData.session = session;
			threadData.result = CKR_OK;
#endif
			EstEID_log("thread launched");
#ifdef _WIN32
			/*
			NB! Due to Firefox for Windows specific behaviour C_Login() is launched from main thread
			and UI code is running in separate thread if running on Windows.
			*/
			EstEID_PINPromptDataEx pinPromptDataEx;
			pinPromptDataEx.pinPromptData = pinPromptData;
			pinPromptDataEx.message = message;
			pinPromptDataEx.name = name;
			CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&EstEID_pinPadLogin, (LPVOID)&pinPromptDataEx, 0, NULL);
			loginResult = fl->C_Login(session, CKU_USER, NULL, 0);
			closePinPadModalSheet();
#else
			FAIL_IF_PTHREAD_ERROR("pthread_create", pthread_create(&pinpad_thread, NULL, EstEID_pinPadLogin, (void*)&threadData));
			pinPromptData.promptFunction(pinPromptData.nativeWindowHandle, name, message, 0, isPinPad);
			loginResult = threadData.result;
#endif
			EstEID_log("pinpad sheet/dialog closed");			
			if (loginResult == CKR_FUNCTION_CANCELED) {
				setUserCancelErrorCodeAndMessage();				
				CLOSE_SESSION_AND_RETURN(FAILURE);
			}
		}
		EstEID_log("loginResult = %s", pkcs11_error_message(loginResult));
		switch (loginResult) {
			case CKR_PIN_LOCKED:
				blocked = TRUE;
			case CKR_PIN_INCORRECT:
			case CKR_PIN_INVALID:
			case CKR_PIN_LEN_RANGE:
				EstEID_log("this was attempt %i, loginResult causes to run next round", attempt);
				continue;
			default:
				if (EstEID_CK_failure("C_Login", loginResult)) CLOSE_SESSION_AND_RETURN(FAILURE);
		}
		break; // Login successful - correct PIN supplied
	}

	return EstEID_RealSign(session, signature, signatureLength, hash, hashLength, name);
}
Beispiel #28
0
// Extract the information about the private RSA key and save it in the token
int dbRSAPriv2session(sqlite3* /*db*/, CK_OBJECT_HANDLE objectID, CK_SESSION_HANDLE hSession)
{
	int result = 0;
	int i;
	CK_OBJECT_HANDLE hKey;
	CK_RV rv;

	CK_ATTRIBUTE privTemplate[] = {
		{ CKA_CLASS,			NULL,	0 },
		{ CKA_TOKEN,			NULL,	0 },
		{ CKA_PRIVATE,			NULL,	0 },
		{ CKA_MODIFIABLE,		NULL,	0 },
		{ CKA_LABEL,			NULL,	0 },
		{ CKA_KEY_TYPE,			NULL,	0 },
		{ CKA_ID,			NULL,	0 },
		{ CKA_START_DATE,		NULL,	0 },
		{ CKA_END_DATE,			NULL,	0 },
		{ CKA_DERIVE,			NULL,	0 },
		{ CKA_SUBJECT,			NULL,	0 },
		{ CKA_SENSITIVE,		NULL,	0 },
		{ CKA_DECRYPT,			NULL,	0 },
		{ CKA_SIGN,			NULL,	0 },
		{ CKA_SIGN_RECOVER,		NULL,	0 },
		{ CKA_UNWRAP,			NULL,	0 },
		{ CKA_EXTRACTABLE,		NULL,	0 },
		{ CKA_WRAP_WITH_TRUSTED,	NULL,	0 },
		{ CKA_MODULUS,			NULL,	0 },
		{ CKA_PUBLIC_EXPONENT,		NULL,	0 },
		{ CKA_PRIVATE_EXPONENT,		NULL,	0 },
		{ CKA_PRIME_1,			NULL,	0 },
		{ CKA_PRIME_2,			NULL,	0 }
// SoftHSM v1 did not store these values
//		{ CKA_EXPONENT_1,		NULL,	0 },
//		{ CKA_EXPONENT_2,		NULL,	0 },
//		{ CKA_COEFFICIENT,		NULL,	0 }
	};

	for (i = 0; i < 23; i++)
	{
		result = getAttribute(objectID, &privTemplate[i]);
		if (result)
		{
			freeTemplate(privTemplate, 23);
			return 1;
		}
	}

	rv = p11->C_CreateObject(hSession, privTemplate, 23, &hKey);
	if (rv != CKR_OK)
	{
		fprintf(stderr, "ERROR %X: Could not save the private key in the token. "
				"Skipping object %lu\n", (unsigned int)rv, objectID);
		result = 1;
	}
	else
	{
		printf("Object %lu has been migrated\n", objectID);
	}

	freeTemplate(privTemplate, 23);

	return result;
}
Beispiel #29
0
// The main function
int main(int argc, char* argv[])
{
	int option_index = 0;
	int opt;

	char* dbPath = NULL;
	char* userPIN = NULL;
	char* module = NULL;
	char* slot = NULL;
	char* serial = NULL;
	char* token = NULL;
	char *errMsg = NULL;
	int noPublicKey = 0;

	int result = 0;
	CK_RV rv;

	moduleHandle = NULL;
	p11 = NULL;
	CK_SLOT_ID slotID = 0;

	if (argc == 1)
	{
		usage();
		exit(0);
	}

	while ((opt = getopt_long(argc, argv, "hv", long_options, &option_index)) != -1)
	{
		switch (opt)
		{
			case OPT_DB:
				dbPath = optarg;
				break;
			case OPT_SLOT:
				slot = optarg;
				break;
			case OPT_SERIAL:
				serial = optarg;
				break;
			case OPT_TOKEN:
				token = optarg;
				break;
			case OPT_MODULE:
				module = optarg;
				break;
			case OPT_NO_PUBLIC_KEY:
				noPublicKey = 1;
				break;
			case OPT_PIN:
				userPIN = optarg;
				break;
			case OPT_VERSION:
			case 'v':
				printf("%s\n", PACKAGE_VERSION);
				exit(0);
				break;
			case OPT_HELP:
			case 'h':
			default:
				usage();
				exit(0);
				break;
		}
	}

	// Get a pointer to the function list for PKCS#11 library
	CK_C_GetFunctionList pGetFunctionList = loadLibrary(module, &moduleHandle, &errMsg);
	if (pGetFunctionList == NULL)
	{
		fprintf(stderr, "ERROR: Could not load the library: %s\n", errMsg);
		exit(1);
	}

	// Load the function list
	(*pGetFunctionList)(&p11);

	// Initialize the library
	rv = p11->C_Initialize(NULL_PTR);
	if (rv != CKR_OK)
	{
		fprintf(stderr, "ERROR: Could not initialize the library.\n");
		exit(1);
	}

	// Get the slotID
	result = findSlot(slot, serial, token, slotID);

	if (!result)
	{
		// Migrate the database
		result = migrate(dbPath, slotID, userPIN, noPublicKey);
	}

	// Finalize the library
	p11->C_Finalize(NULL_PTR);
	unloadLibrary(moduleHandle);

	return result;
}
Beispiel #30
0
int
rmain
(
  int argc,
  char *argv[]
)
{
  char *argv0 = argv[0];
  PRLibrary *lib;
  CK_C_GetFunctionList gfl;
  CK_FUNCTION_LIST_PTR epv = (CK_FUNCTION_LIST_PTR)NULL;
  CK_RV ck_rv;
  CK_INFO info;
  CK_ULONG nSlots;
  CK_SLOT_ID *pSlots;
  CK_ULONG i;
  CK_C_INITIALIZE_ARGS ia, *iap;

  (void)memset(&ia, 0, sizeof(CK_C_INITIALIZE_ARGS));
  iap = (CK_C_INITIALIZE_ARGS *)NULL;
  while( argv++, --argc ) {
    if( '-' == argv[0][0] ) {
      switch( argv[0][1] ) {
      case 'i':
        iap = &ia;
        if( ((char *)NULL != argv[1]) && ('-' != argv[1][0]) ) {
#ifdef WITH_NSS
          ia.pConfig = argv[1];
          ia.ulConfigLen = strlen(argv[1]);
          argv++, --argc;
#else
          return usage(argv0);
#endif /* WITH_NSS */
        }
        break;
      case '-':
        argv++, --argc;
        goto endargs;
      default:
        return usage(argv0);
      }
    } else {
      break;
    }
  }
 endargs:;

  if( 1 != argc ) {
    return usage(argv0);
  }

  lib = PR_LoadLibrary(argv[0]);
  if( (PRLibrary *)NULL == lib ) {
    PR_fprintf(PR_STDERR, "Can't load %s: %ld, %ld\n", argv[1], PR_GetError(), PR_GetOSError());
    return 1;
  }

  gfl = (CK_C_GetFunctionList)PR_FindSymbol(lib, "C_GetFunctionList");
  if( (CK_C_GetFunctionList)NULL == gfl ) {
    PR_fprintf(PR_STDERR, "Can't find C_GetFunctionList in %s: %ld, %ld\n", argv[1], 
               PR_GetError(), PR_GetOSError());
    return 1;
  }

  ck_rv = (*gfl)(&epv);
  if( CKR_OK != ck_rv ) {
    PR_fprintf(PR_STDERR, "CK_GetFunctionList returned 0x%08x\n", ck_rv);
    return 1;
  }

  PR_fprintf(PR_STDOUT, "Module %s loaded, epv = 0x%08x.\n\n", argv[1], (CK_ULONG)epv);

  /* C_Initialize */
  ck_rv = epv->C_Initialize(iap);
  if( CKR_OK != ck_rv ) {
    PR_fprintf(PR_STDERR, "C_Initialize returned 0x%08x\n", ck_rv);
    return 1;
  }

  /* C_GetInfo */
  (void)memset(&info, 0, sizeof(CK_INFO));
  ck_rv = epv->C_GetInfo(&info);
  if( CKR_OK != ck_rv ) {
    PR_fprintf(PR_STDERR, "C_GetInfo returned 0x%08x\n", ck_rv);
    return 1;
  }

  PR_fprintf(PR_STDOUT, "Module Info:\n");
  PR_fprintf(PR_STDOUT, "    cryptokiVersion = %lu.%02lu\n", 
             (PRUint32)info.cryptokiVersion.major, (PRUint32)info.cryptokiVersion.minor);
  PR_fprintf(PR_STDOUT, "    manufacturerID = \"%.32s\"\n", info.manufacturerID);
  PR_fprintf(PR_STDOUT, "    flags = 0x%08lx\n", info.flags);
  PR_fprintf(PR_STDOUT, "    libraryDescription = \"%.32s\"\n", info.libraryDescription);
  PR_fprintf(PR_STDOUT, "    libraryVersion = %lu.%02lu\n", 
             (PRUint32)info.libraryVersion.major, (PRUint32)info.libraryVersion.minor);
  PR_fprintf(PR_STDOUT, "\n");

  /* C_GetSlotList */
  nSlots = 0;
  ck_rv = epv->C_GetSlotList(CK_FALSE, (CK_SLOT_ID_PTR)CK_NULL_PTR, &nSlots);
  switch( ck_rv ) {
  case CKR_BUFFER_TOO_SMALL:
  case CKR_OK:
    break;
  default:
    PR_fprintf(PR_STDERR, "C_GetSlotList(FALSE, NULL, ) returned 0x%08x\n", ck_rv);
    return 1;
  }

  PR_fprintf(PR_STDOUT, "There are %lu slots.\n", nSlots);

  pSlots = (CK_SLOT_ID_PTR)PR_Calloc(nSlots, sizeof(CK_SLOT_ID));
  if( (CK_SLOT_ID_PTR)NULL == pSlots ) {
    PR_fprintf(PR_STDERR, "[memory allocation of %lu bytes failed]\n", nSlots * sizeof(CK_SLOT_ID));
    return 1;
  }

  ck_rv = epv->C_GetSlotList(CK_FALSE, pSlots, &nSlots);
  if( CKR_OK != ck_rv ) {
    PR_fprintf(PR_STDERR, "C_GetSlotList(FALSE, , ) returned 0x%08x\n", ck_rv);
    return 1;
  }

  for( i = 0; i < nSlots; i++ ) {
    PR_fprintf(PR_STDOUT, "    [%lu]: CK_SLOT_ID = %lu\n", (i+1), pSlots[i]);
  }

  PR_fprintf(PR_STDOUT, "\n");

  /* C_GetSlotInfo */
  for( i = 0; i < nSlots; i++ ) {
    CK_SLOT_INFO sinfo;

    PR_fprintf(PR_STDOUT, "[%lu]: CK_SLOT_ID = %lu\n", (i+1), pSlots[i]);

    (void)memset(&sinfo, 0, sizeof(CK_SLOT_INFO));
    ck_rv = epv->C_GetSlotInfo(pSlots[i], &sinfo);
    if( CKR_OK != ck_rv ) {
      PR_fprintf(PR_STDERR, "C_GetSlotInfo(%lu, ) returned 0x%08x\n", pSlots[i], ck_rv);
      return 1;
    }

    PR_fprintf(PR_STDOUT, "    Slot Info:\n");
    PR_fprintf(PR_STDOUT, "        slotDescription = \"%.64s\"\n", sinfo.slotDescription);
    PR_fprintf(PR_STDOUT, "        manufacturerID = \"%.32s\"\n", sinfo.manufacturerID);
    PR_fprintf(PR_STDOUT, "        flags = 0x%08lx\n", sinfo.flags);
    PR_fprintf(PR_STDOUT, "            -> TOKEN PRESENT = %s\n", 
               sinfo.flags & CKF_TOKEN_PRESENT ? "TRUE" : "FALSE");
    PR_fprintf(PR_STDOUT, "            -> REMOVABLE DEVICE = %s\n",
               sinfo.flags & CKF_REMOVABLE_DEVICE ? "TRUE" : "FALSE");
    PR_fprintf(PR_STDOUT, "            -> HW SLOT = %s\n", 
               sinfo.flags & CKF_HW_SLOT ? "TRUE" : "FALSE");
    PR_fprintf(PR_STDOUT, "        hardwareVersion = %lu.%02lu\n", 
               (PRUint32)sinfo.hardwareVersion.major, (PRUint32)sinfo.hardwareVersion.minor);
    PR_fprintf(PR_STDOUT, "        firmwareVersion = %lu.%02lu\n",
               (PRUint32)sinfo.firmwareVersion.major, (PRUint32)sinfo.firmwareVersion.minor);

    if( sinfo.flags & CKF_TOKEN_PRESENT ) {
      CK_TOKEN_INFO tinfo;
      CK_MECHANISM_TYPE *pMechanismList;
      CK_ULONG nMechanisms = 0;
      CK_ULONG j;

      (void)memset(&tinfo, 0, sizeof(CK_TOKEN_INFO));
      ck_rv = epv->C_GetTokenInfo(pSlots[i], &tinfo);
      if( CKR_OK != ck_rv ) {
        PR_fprintf(PR_STDERR, "C_GetTokenInfo(%lu, ) returned 0x%08x\n", pSlots[i], ck_rv);
        return 1;
      }

      PR_fprintf(PR_STDOUT, "    Token Info:\n");
      PR_fprintf(PR_STDOUT, "        label = \"%.32s\"\n", tinfo.label);
      PR_fprintf(PR_STDOUT, "        manufacturerID = \"%.32s\"\n", tinfo.manufacturerID);
      PR_fprintf(PR_STDOUT, "        model = \"%.16s\"\n", tinfo.model);
      PR_fprintf(PR_STDOUT, "        serialNumber = \"%.16s\"\n", tinfo.serialNumber);
      PR_fprintf(PR_STDOUT, "        flags = 0x%08lx\n", tinfo.flags);
      PR_fprintf(PR_STDOUT, "            -> RNG = %s\n",
                 tinfo.flags & CKF_RNG ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "            -> WRITE PROTECTED = %s\n",
                 tinfo.flags & CKF_WRITE_PROTECTED ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "            -> LOGIN REQUIRED = %s\n",
                 tinfo.flags & CKF_LOGIN_REQUIRED ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "            -> USER PIN INITIALIZED = %s\n",
                 tinfo.flags & CKF_USER_PIN_INITIALIZED ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "            -> RESTORE KEY NOT NEEDED = %s\n",
                 tinfo.flags & CKF_RESTORE_KEY_NOT_NEEDED ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "            -> CLOCK ON TOKEN = %s\n",
                 tinfo.flags & CKF_CLOCK_ON_TOKEN ? "TRUE" : "FALSE");
#ifdef CKF_SUPPORTS_PARALLEL
      PR_fprintf(PR_STDOUT, "            -> SUPPORTS PARALLEL = %s\n",
                 tinfo.flags & CKF_SUPPORTS_PARALLEL ? "TRUE" : "FALSE");
#endif /* CKF_SUPPORTS_PARALLEL */
      PR_fprintf(PR_STDOUT, "            -> PROTECTED AUTHENTICATION PATH = %s\n",
                 tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "            -> DUAL_CRYPTO_OPERATIONS = %s\n", 
                 tinfo.flags & CKF_DUAL_CRYPTO_OPERATIONS ? "TRUE" : "FALSE");
      PR_fprintf(PR_STDOUT, "        ulMaxSessionCount = %lu\n", tinfo.ulMaxSessionCount);
      PR_fprintf(PR_STDOUT, "        ulSessionCount = %lu\n", tinfo.ulSessionCount);
      PR_fprintf(PR_STDOUT, "        ulMaxRwSessionCount = %lu\n", tinfo.ulMaxRwSessionCount);
      PR_fprintf(PR_STDOUT, "        ulRwSessionCount = %lu\n", tinfo.ulRwSessionCount);
      PR_fprintf(PR_STDOUT, "        ulMaxPinLen = %lu\n", tinfo.ulMaxPinLen);
      PR_fprintf(PR_STDOUT, "        ulMinPinLen = %lu\n", tinfo.ulMinPinLen);
      PR_fprintf(PR_STDOUT, "        ulTotalPublicMemory = %lu\n", tinfo.ulTotalPublicMemory);
      PR_fprintf(PR_STDOUT, "        ulFreePublicMemory = %lu\n", tinfo.ulFreePublicMemory);
      PR_fprintf(PR_STDOUT, "        ulTotalPrivateMemory = %lu\n", tinfo.ulTotalPrivateMemory);
      PR_fprintf(PR_STDOUT, "        ulFreePrivateMemory = %lu\n", tinfo.ulFreePrivateMemory);
      PR_fprintf(PR_STDOUT, "        hardwareVersion = %lu.%02lu\n", 
                 (PRUint32)tinfo.hardwareVersion.major, (PRUint32)tinfo.hardwareVersion.minor);
      PR_fprintf(PR_STDOUT, "        firmwareVersion = %lu.%02lu\n",
                 (PRUint32)tinfo.firmwareVersion.major, (PRUint32)tinfo.firmwareVersion.minor);
      PR_fprintf(PR_STDOUT, "        utcTime = \"%.16s\"\n", tinfo.utcTime);


      ck_rv = epv->C_GetMechanismList(pSlots[i], (CK_MECHANISM_TYPE_PTR)CK_NULL_PTR, &nMechanisms);
      switch( ck_rv ) {
      case CKR_BUFFER_TOO_SMALL:
      case CKR_OK:
        break;
      default:
        PR_fprintf(PR_STDERR, "C_GetMechanismList(%lu, NULL, ) returned 0x%08x\n", pSlots[i], ck_rv);
        return 1;
      }

      PR_fprintf(PR_STDOUT, "    %lu mechanisms:\n", nMechanisms);

      pMechanismList = (CK_MECHANISM_TYPE_PTR)PR_Calloc(nMechanisms, sizeof(CK_MECHANISM_TYPE));
      if( (CK_MECHANISM_TYPE_PTR)NULL == pMechanismList ) {
        PR_fprintf(PR_STDERR, "[memory allocation of %lu bytes failed]\n", 
                   nMechanisms * sizeof(CK_MECHANISM_TYPE));
        return 1;
      }

      ck_rv = epv->C_GetMechanismList(pSlots[i], pMechanismList, &nMechanisms);
      if( CKR_OK != ck_rv ) {
        PR_fprintf(PR_STDERR, "C_GetMechanismList(%lu, , ) returned 0x%08x\n", pSlots[i], ck_rv);
        return 1;
      }

      for( j = 0; j < nMechanisms; j++ ) {
        PR_fprintf(PR_STDOUT, "        {%lu}: CK_MECHANISM_TYPE = %lu\n", (j+1), pMechanismList[j]);
      }

      PR_fprintf(PR_STDOUT, "\n");

      for( j = 0; j < nMechanisms; j++ ) {
        CK_MECHANISM_INFO minfo;

        (void)memset(&minfo, 0, sizeof(CK_MECHANISM_INFO));
        ck_rv = epv->C_GetMechanismInfo(pSlots[i], pMechanismList[j], &minfo);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_GetMechanismInfo(%lu, %lu, ) returned 0x%08x\n", pSlots[i], 
                     pMechanismList[j]);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    [%lu]: CK_MECHANISM_TYPE = %lu\n", (j+1), pMechanismList[j]);
        PR_fprintf(PR_STDOUT, "    ulMinKeySize = %lu\n", minfo.ulMinKeySize);
        PR_fprintf(PR_STDOUT, "    ulMaxKeySize = %lu\n", minfo.ulMaxKeySize);
        PR_fprintf(PR_STDOUT, "    flags = 0x%08x\n", minfo.flags);
        PR_fprintf(PR_STDOUT, "        -> HW = %s\n", minfo.flags & CKF_HW ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> ENCRYPT = %s\n", minfo.flags & CKF_ENCRYPT ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> DECRYPT = %s\n", minfo.flags & CKF_DECRYPT ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> DIGEST = %s\n", minfo.flags & CKF_DIGEST ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> SIGN = %s\n", minfo.flags & CKF_SIGN ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> SIGN_RECOVER = %s\n", minfo.flags & CKF_SIGN_RECOVER ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> VERIFY = %s\n", minfo.flags & CKF_VERIFY ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> VERIFY_RECOVER = %s\n", minfo.flags & CKF_VERIFY_RECOVER ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> GENERATE = %s\n", minfo.flags & CKF_GENERATE ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> GENERATE_KEY_PAIR = %s\n", minfo.flags & CKF_GENERATE_KEY_PAIR ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> WRAP = %s\n", minfo.flags & CKF_WRAP ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> UNWRAP = %s\n", minfo.flags & CKF_UNWRAP ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> DERIVE = %s\n", minfo.flags & CKF_DERIVE ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "        -> EXTENSION = %s\n", minfo.flags & CKF_EXTENSION ? "TRUE" : "FALSE");

        PR_fprintf(PR_STDOUT, "\n");
      }

      if( tinfo.flags & CKF_LOGIN_REQUIRED ) {
        PR_fprintf(PR_STDERR, "*** LOGIN REQUIRED but not yet implemented ***\n");
        /* all the stuff about logging in as SO and setting the user pin if needed, etc. */
        return 2;
      }

      /* session to find objects */
      {
        CK_SESSION_HANDLE h = (CK_SESSION_HANDLE)0;
        CK_SESSION_INFO sinfo;
        CK_ATTRIBUTE_PTR pTemplate;
        CK_ULONG tnObjects = 0;

        ck_rv = epv->C_OpenSession(pSlots[i], CKF_SERIAL_SESSION, (CK_VOID_PTR)CK_NULL_PTR, (CK_NOTIFY)CK_NULL_PTR, &h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_OpenSession(%lu, CKF_SERIAL_SESSION, , ) returned 0x%08x\n", pSlots[i], ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Opened a session: handle = 0x%08x\n", h);

        (void)memset(&sinfo, 0, sizeof(CK_SESSION_INFO));
        ck_rv = epv->C_GetSessionInfo(h, &sinfo);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDOUT, "C_GetSessionInfo(%lu, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    SESSION INFO:\n");
        PR_fprintf(PR_STDOUT, "        slotID = %lu\n", sinfo.slotID);
        PR_fprintf(PR_STDOUT, "        state = %lu\n", sinfo.state);
        PR_fprintf(PR_STDOUT, "        flags = 0x%08x\n", sinfo.flags);
#ifdef CKF_EXCLUSIVE_SESSION
        PR_fprintf(PR_STDOUT, "            -> EXCLUSIVE SESSION = %s\n", sinfo.flags & CKF_EXCLUSIVE_SESSION ? "TRUE" : "FALSE");
#endif /* CKF_EXCLUSIVE_SESSION */
        PR_fprintf(PR_STDOUT, "            -> RW SESSION = %s\n", sinfo.flags & CKF_RW_SESSION ? "TRUE" : "FALSE");
        PR_fprintf(PR_STDOUT, "            -> SERIAL SESSION = %s\n", sinfo.flags & CKF_SERIAL_SESSION ? "TRUE" : "FALSE");
#ifdef CKF_INSERTION_CALLBACK
        PR_fprintf(PR_STDOUT, "            -> INSERTION CALLBACK = %s\n", sinfo.flags & CKF_INSERTION_CALLBACK ? "TRUE" : "FALSE");
#endif /* CKF_INSERTION_CALLBACK */
        PR_fprintf(PR_STDOUT, "        ulDeviceError = %lu\n", sinfo.ulDeviceError);
        PR_fprintf(PR_STDOUT, "\n");

        ck_rv = epv->C_FindObjectsInit(h, (CK_ATTRIBUTE_PTR)CK_NULL_PTR, 0);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDOUT, "C_FindObjectsInit(%lu, NULL_PTR, 0) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        pTemplate = (CK_ATTRIBUTE_PTR)PR_Calloc(number_of_all_known_attribute_types, sizeof(CK_ATTRIBUTE));
        if( (CK_ATTRIBUTE_PTR)NULL == pTemplate ) {
          PR_fprintf(PR_STDERR, "[memory allocation of %lu bytes failed]\n", 
                     number_of_all_known_attribute_types * sizeof(CK_ATTRIBUTE));
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    All objects:\n");

        while(1) {
          CK_OBJECT_HANDLE o = (CK_OBJECT_HANDLE)0;
          CK_ULONG nObjects = 0;
          CK_ULONG k;
          CK_ULONG nAttributes = 0;
          CK_ATTRIBUTE_PTR pT2;
          CK_ULONG l;

          ck_rv = epv->C_FindObjects(h, &o, 1, &nObjects);
          if( CKR_OK != ck_rv ) {
            PR_fprintf(PR_STDERR, "C_FindObjects(%lu, , 1, ) returned 0x%08x\n", h, ck_rv);
            return 1;
          }

          if( 0 == nObjects ) {
            PR_fprintf(PR_STDOUT, "\n");
            break;
          }

          tnObjects++;

          PR_fprintf(PR_STDOUT, "        OBJECT HANDLE %lu:\n", o);

          for( k = 0; k < number_of_all_known_attribute_types; k++ ) {
            pTemplate[k].type = all_known_attribute_types[k];
            pTemplate[k].pValue = (CK_VOID_PTR)CK_NULL_PTR;
            pTemplate[k].ulValueLen = 0;
          }

          ck_rv = epv->C_GetAttributeValue(h, o, pTemplate, number_of_all_known_attribute_types);
          switch( ck_rv ) {
          case CKR_OK:
          case CKR_ATTRIBUTE_SENSITIVE:
          case CKR_ATTRIBUTE_TYPE_INVALID:
          case CKR_BUFFER_TOO_SMALL:
            break;
          default:
            PR_fprintf(PR_STDERR, "C_GetAtributeValue(%lu, %lu, {all attribute types}, %lu) returned 0x%08x\n",
                       h, o, number_of_all_known_attribute_types, ck_rv);
            return 1;
          }

          for( k = 0; k < number_of_all_known_attribute_types; k++ ) {
            if( -1 != (CK_LONG)pTemplate[k].ulValueLen ) {
              nAttributes++;
            }
          }

          if( 1 ) {
            PR_fprintf(PR_STDOUT, "            %lu attributes:\n", nAttributes);
            for( k = 0; k < number_of_all_known_attribute_types; k++ ) {
              if( -1 != (CK_LONG)pTemplate[k].ulValueLen ) {
                PR_fprintf(PR_STDOUT, "                0x%08x (len = %lu)\n", pTemplate[k].type, 
                           pTemplate[k].ulValueLen);
              }
            }
            PR_fprintf(PR_STDOUT, "\n");
          }

          pT2 = (CK_ATTRIBUTE_PTR)PR_Calloc(nAttributes, sizeof(CK_ATTRIBUTE));
          if( (CK_ATTRIBUTE_PTR)NULL == pT2 ) {
            PR_fprintf(PR_STDERR, "[memory allocation of %lu bytes failed]\n", 
                       nAttributes * sizeof(CK_ATTRIBUTE));
            return 1;
          }

          for( l = 0, k = 0; k < number_of_all_known_attribute_types; k++ ) {
            if( -1 != (CK_LONG)pTemplate[k].ulValueLen ) {
              pT2[l].type = pTemplate[k].type;
              pT2[l].ulValueLen = pTemplate[k].ulValueLen;
              pT2[l].pValue = (CK_VOID_PTR)PR_Malloc(pT2[l].ulValueLen);
              if( (CK_VOID_PTR)NULL == pT2[l].pValue ) {
                PR_fprintf(PR_STDERR, "[memory allocation of %lu bytes failed]\n", pT2[l].ulValueLen);
                return 1;
              }
              l++;
            }
          }

          PR_ASSERT( l == nAttributes );

          ck_rv = epv->C_GetAttributeValue(h, o, pT2, nAttributes);
          switch( ck_rv ) {
          case CKR_OK:
          case CKR_ATTRIBUTE_SENSITIVE:
          case CKR_ATTRIBUTE_TYPE_INVALID:
          case CKR_BUFFER_TOO_SMALL:
            break;
          default:
            PR_fprintf(PR_STDERR, "C_GetAtributeValue(%lu, %lu, {existent attribute types}, %lu) returned 0x%08x\n",
                       h, o, nAttributes, ck_rv);
            return 1;
          }

          for( l = 0; l < nAttributes; l++ ) {
            PR_fprintf(PR_STDOUT, "            type = 0x%08x, len = %ld", pT2[l].type, (CK_LONG)pT2[l].ulValueLen);
            if( -1 == (CK_LONG)pT2[l].ulValueLen ) {
              ;
            } else {
              CK_ULONG m;

              if( pT2[l].ulValueLen <= 8 ) {
                PR_fprintf(PR_STDOUT, ", value = ");
              } else {
                PR_fprintf(PR_STDOUT, ", value = \n                ");
              }

              for( m = 0; (m < pT2[l].ulValueLen) && (m < 20); m++ ) {
                PR_fprintf(PR_STDOUT, "%02x", (CK_ULONG)(0xff & ((CK_CHAR_PTR)pT2[l].pValue)[m]));
              }

              PR_fprintf(PR_STDOUT, " ");

              for( m = 0; (m < pT2[l].ulValueLen) && (m < 20); m++ ) {
                CK_CHAR c = ((CK_CHAR_PTR)pT2[l].pValue)[m];
                if( (c < 0x20) || (c >= 0x7f) ) {
                  c = '.';
                }
                PR_fprintf(PR_STDOUT, "%c", c);
              }
            }

            PR_fprintf(PR_STDOUT, "\n");
          }
          
          PR_fprintf(PR_STDOUT, "\n");

          for( l = 0; l < nAttributes; l++ ) {
            PR_Free(pT2[l].pValue);
          }
          PR_Free(pT2);
        } /* while(1) */

        ck_rv = epv->C_FindObjectsFinal(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    (%lu objects total)\n", tnObjects);

        ck_rv = epv->C_CloseSession(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CloseSession(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }
      } /* session to find objects */

      /* session to create, find, and delete a couple session objects */
      {
        CK_SESSION_HANDLE h = (CK_SESSION_HANDLE)0;
        CK_ATTRIBUTE one[7], two[7], three[7], delta[1], mask[1];
        CK_OBJECT_CLASS cko_data = CKO_DATA;
        CK_BBOOL false = CK_FALSE, true = CK_TRUE;
        char *key = "TEST PROGRAM";
        CK_ULONG key_len = strlen(key);
        CK_OBJECT_HANDLE hOneIn = (CK_OBJECT_HANDLE)0, hTwoIn = (CK_OBJECT_HANDLE)0, 
          hThreeIn = (CK_OBJECT_HANDLE)0, hDeltaIn = (CK_OBJECT_HANDLE)0;
        CK_OBJECT_HANDLE found[10];
        CK_ULONG nFound;

        ck_rv = epv->C_OpenSession(pSlots[i], CKF_SERIAL_SESSION, (CK_VOID_PTR)CK_NULL_PTR, (CK_NOTIFY)CK_NULL_PTR, &h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_OpenSession(%lu, CKF_SERIAL_SESSION, , ) returned 0x%08x\n", pSlots[i], ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Opened a session: handle = 0x%08x\n", h);

        one[0].type = CKA_CLASS;
        one[0].pValue = &cko_data;
        one[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        one[1].type = CKA_TOKEN;
        one[1].pValue = &false;
        one[1].ulValueLen = sizeof(CK_BBOOL);
        one[2].type = CKA_PRIVATE;
        one[2].pValue = &false;
        one[2].ulValueLen = sizeof(CK_BBOOL);
        one[3].type = CKA_MODIFIABLE;
        one[3].pValue = &true;
        one[3].ulValueLen = sizeof(CK_BBOOL);
        one[4].type = CKA_LABEL;
        one[4].pValue = "Test data object one";
        one[4].ulValueLen = strlen(one[4].pValue);
        one[5].type = CKA_APPLICATION;
        one[5].pValue = key;
        one[5].ulValueLen = key_len;
        one[6].type = CKA_VALUE;
        one[6].pValue = "Object one";
        one[6].ulValueLen = strlen(one[6].pValue);

        two[0].type = CKA_CLASS;
        two[0].pValue = &cko_data;
        two[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        two[1].type = CKA_TOKEN;
        two[1].pValue = &false;
        two[1].ulValueLen = sizeof(CK_BBOOL);
        two[2].type = CKA_PRIVATE;
        two[2].pValue = &false;
        two[2].ulValueLen = sizeof(CK_BBOOL);
        two[3].type = CKA_MODIFIABLE;
        two[3].pValue = &true;
        two[3].ulValueLen = sizeof(CK_BBOOL);
        two[4].type = CKA_LABEL;
        two[4].pValue = "Test data object two";
        two[4].ulValueLen = strlen(two[4].pValue);
        two[5].type = CKA_APPLICATION;
        two[5].pValue = key;
        two[5].ulValueLen = key_len;
        two[6].type = CKA_VALUE;
        two[6].pValue = "Object two";
        two[6].ulValueLen = strlen(two[6].pValue);

        three[0].type = CKA_CLASS;
        three[0].pValue = &cko_data;
        three[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        three[1].type = CKA_TOKEN;
        three[1].pValue = &false;
        three[1].ulValueLen = sizeof(CK_BBOOL);
        three[2].type = CKA_PRIVATE;
        three[2].pValue = &false;
        three[2].ulValueLen = sizeof(CK_BBOOL);
        three[3].type = CKA_MODIFIABLE;
        three[3].pValue = &true;
        three[3].ulValueLen = sizeof(CK_BBOOL);
        three[4].type = CKA_LABEL;
        three[4].pValue = "Test data object three";
        three[4].ulValueLen = strlen(three[4].pValue);
        three[5].type = CKA_APPLICATION;
        three[5].pValue = key;
        three[5].ulValueLen = key_len;
        three[6].type = CKA_VALUE;
        three[6].pValue = "Object three";
        three[6].ulValueLen = strlen(three[6].pValue);

        ck_rv = epv->C_CreateObject(h, one, 7, &hOneIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, one, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created object one: handle = %lu\n", hOneIn);

        ck_rv = epv->C_CreateObject(h, two, 7, &hTwoIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, two, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created object two: handle = %lu\n", hTwoIn);

        ck_rv = epv->C_CreateObject(h, three, 7, &hThreeIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, three, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created object three: handle = %lu\n", hThreeIn);

        delta[0].type = CKA_VALUE;
        delta[0].pValue = "Copied object";
        delta[0].ulValueLen = strlen(delta[0].pValue);

        ck_rv = epv->C_CopyObject(h, hThreeIn, delta, 1, &hDeltaIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CopyObject(%lu, %lu, delta, 1, ) returned 0x%08x\n", 
                     h, hThreeIn, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Copied object three: new handle = %lu\n", hDeltaIn);

        mask[0].type = CKA_APPLICATION;
        mask[0].pValue = key;
        mask[0].ulValueLen = key_len;

        ck_rv = epv->C_FindObjectsInit(h, mask, 1);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsInit(%lu, mask, 1) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        (void)memset(&found, 0, sizeof(found));
        nFound = 0;
        ck_rv = epv->C_FindObjects(h, found, 10, &nFound);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjects(%lu,, 10, ) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        if( 4 != nFound ) {
          PR_fprintf(PR_STDERR, "Found %lu objects, not 4.\n", nFound);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Found 4 objects: %lu, %lu, %lu, %lu\n", 
                   found[0], found[1], found[2], found[3]);

        ck_rv = epv->C_FindObjectsFinal(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        ck_rv = epv->C_DestroyObject(h, hThreeIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_DestroyObject(%lu, %lu) returned 0x%08x\n", h, hThreeIn, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Destroyed object three (handle = %lu)\n", hThreeIn);

        delta[0].type = CKA_APPLICATION;
        delta[0].pValue = "Changed application";
        delta[0].ulValueLen = strlen(delta[0].pValue);

        ck_rv = epv->C_SetAttributeValue(h, hTwoIn, delta, 1);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_SetAttributeValue(%lu, %lu, delta, 1) returned 0x%08x\n", 
                     h, hTwoIn, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Changed object two (handle = %lu).\n", hTwoIn);

        /* Can another session find these session objects? */
        {
          CK_SESSION_HANDLE h2 = (CK_SESSION_HANDLE)0;

          ck_rv = epv->C_OpenSession(pSlots[i], CKF_SERIAL_SESSION, (CK_VOID_PTR)CK_NULL_PTR, (CK_NOTIFY)CK_NULL_PTR, &h2);
          if( CKR_OK != ck_rv ) {
            PR_fprintf(PR_STDERR, "C_OpenSession(%lu, CKF_SERIAL_SESSION, , ) returned 0x%08x\n", pSlots[i], ck_rv);
            return 1;
          }

          PR_fprintf(PR_STDOUT, "    Opened a second session: handle = 0x%08x\n", h2);

          /* mask is still the same */

          ck_rv = epv->C_FindObjectsInit(h2, mask, 1);
          if( CKR_OK != ck_rv ) {
            PR_fprintf(PR_STDERR, "C_FindObjectsInit(%lu, mask, 1) returned 0x%08x\n", 
                       h2, ck_rv);
            return 1;
          }

          (void)memset(&found, 0, sizeof(found));
          nFound = 0;
          ck_rv = epv->C_FindObjects(h2, found, 10, &nFound);
          if( CKR_OK != ck_rv ) {
            PR_fprintf(PR_STDERR, "C_FindObjects(%lu,, 10, ) returned 0x%08x\n", 
                       h2, ck_rv);
            return 1;
          }

          if( 2 != nFound ) {
            PR_fprintf(PR_STDERR, "Found %lu objects, not 2.\n", nFound);
            return 1;
          }

          PR_fprintf(PR_STDOUT, "    Found 2 objects: %lu, %lu\n", 
                     found[0], found[1]);

          ck_rv = epv->C_FindObjectsFinal(h2);
          if( CKR_OK != ck_rv ) {
            PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h2, ck_rv);
            return 1;
          }

          /* Leave the session hanging open, we'll CloseAllSessions later */
        } /* Can another session find these session objects? */

        ck_rv = epv->C_CloseAllSessions(pSlots[i]);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CloseAllSessions(%lu) returned 0x%08x\n", pSlots[i], ck_rv);
          return 1;
        }
      } /* session to create, find, and delete a couple session objects */

      /* Might be interesting to do a find here to verify that all session objects are gone. */

      if( tinfo.flags & CKF_WRITE_PROTECTED ) {
        PR_fprintf(PR_STDOUT, "Token is write protected, skipping token-object tests.\n");
      } else {
        CK_SESSION_HANDLE h = (CK_SESSION_HANDLE)0;
        CK_ATTRIBUTE tobj[7], tsobj[7], stobj[7], delta[1], mask[2];
        CK_OBJECT_CLASS cko_data = CKO_DATA;
        CK_BBOOL false = CK_FALSE, true = CK_TRUE;
        char *key = "TEST PROGRAM";
        CK_ULONG key_len = strlen(key);
        CK_OBJECT_HANDLE hTIn = (CK_OBJECT_HANDLE)0, hTSIn = (CK_OBJECT_HANDLE)0, 
          hSTIn = (CK_OBJECT_HANDLE)0, hDeltaIn = (CK_OBJECT_HANDLE)0;
        CK_OBJECT_HANDLE found[10];
        CK_ULONG nFound;

        ck_rv = epv->C_OpenSession(pSlots[i], CKF_SERIAL_SESSION, (CK_VOID_PTR)CK_NULL_PTR, (CK_NOTIFY)CK_NULL_PTR, &h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_OpenSession(%lu, CKF_SERIAL_SESSION, , ) returned 0x%08x\n", pSlots[i], ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Opened a session: handle = 0x%08x\n", h);

        tobj[0].type = CKA_CLASS;
        tobj[0].pValue = &cko_data;
        tobj[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        tobj[1].type = CKA_TOKEN;
        tobj[1].pValue = &true;
        tobj[1].ulValueLen = sizeof(CK_BBOOL);
        tobj[2].type = CKA_PRIVATE;
        tobj[2].pValue = &false;
        tobj[2].ulValueLen = sizeof(CK_BBOOL);
        tobj[3].type = CKA_MODIFIABLE;
        tobj[3].pValue = &true;
        tobj[3].ulValueLen = sizeof(CK_BBOOL);
        tobj[4].type = CKA_LABEL;
        tobj[4].pValue = "Test data object token";
        tobj[4].ulValueLen = strlen(tobj[4].pValue);
        tobj[5].type = CKA_APPLICATION;
        tobj[5].pValue = key;
        tobj[5].ulValueLen = key_len;
        tobj[6].type = CKA_VALUE;
        tobj[6].pValue = "Object token";
        tobj[6].ulValueLen = strlen(tobj[6].pValue);

        tsobj[0].type = CKA_CLASS;
        tsobj[0].pValue = &cko_data;
        tsobj[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        tsobj[1].type = CKA_TOKEN;
        tsobj[1].pValue = &true;
        tsobj[1].ulValueLen = sizeof(CK_BBOOL);
        tsobj[2].type = CKA_PRIVATE;
        tsobj[2].pValue = &false;
        tsobj[2].ulValueLen = sizeof(CK_BBOOL);
        tsobj[3].type = CKA_MODIFIABLE;
        tsobj[3].pValue = &true;
        tsobj[3].ulValueLen = sizeof(CK_BBOOL);
        tsobj[4].type = CKA_LABEL;
        tsobj[4].pValue = "Test data object token->session";
        tsobj[4].ulValueLen = strlen(tsobj[4].pValue);
        tsobj[5].type = CKA_APPLICATION;
        tsobj[5].pValue = key;
        tsobj[5].ulValueLen = key_len;
        tsobj[6].type = CKA_VALUE;
        tsobj[6].pValue = "Object token->session";
        tsobj[6].ulValueLen = strlen(tsobj[6].pValue);

        stobj[0].type = CKA_CLASS;
        stobj[0].pValue = &cko_data;
        stobj[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        stobj[1].type = CKA_TOKEN;
        stobj[1].pValue = &false;
        stobj[1].ulValueLen = sizeof(CK_BBOOL);
        stobj[2].type = CKA_PRIVATE;
        stobj[2].pValue = &false;
        stobj[2].ulValueLen = sizeof(CK_BBOOL);
        stobj[3].type = CKA_MODIFIABLE;
        stobj[3].pValue = &true;
        stobj[3].ulValueLen = sizeof(CK_BBOOL);
        stobj[4].type = CKA_LABEL;
        stobj[4].pValue = "Test data object session->token";
        stobj[4].ulValueLen = strlen(stobj[4].pValue);
        stobj[5].type = CKA_APPLICATION;
        stobj[5].pValue = key;
        stobj[5].ulValueLen = key_len;
        stobj[6].type = CKA_VALUE;
        stobj[6].pValue = "Object session->token";
        stobj[6].ulValueLen = strlen(stobj[6].pValue);

        ck_rv = epv->C_CreateObject(h, tobj, 7, &hTIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, tobj, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created object token: handle = %lu\n", hTIn);

        ck_rv = epv->C_CreateObject(h, tsobj, 7, &hTSIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, tobj, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created object token->session: handle = %lu\n", hTSIn);
        ck_rv = epv->C_CreateObject(h, stobj, 7, &hSTIn);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, tobj, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created object session->token: handle = %lu\n", hSTIn);

        /* I've created two token objects and one session object; find the two */

        mask[0].type = CKA_APPLICATION;
        mask[0].pValue = key;
        mask[0].ulValueLen = key_len;
        mask[1].type = CKA_TOKEN;
        mask[1].pValue = &true;
        mask[1].ulValueLen = sizeof(CK_BBOOL);

        ck_rv = epv->C_FindObjectsInit(h, mask, 2);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsInit(%lu, mask, 2) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        (void)memset(&found, 0, sizeof(found));
        nFound = 0;
        ck_rv = epv->C_FindObjects(h, found, 10, &nFound);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjects(%lu,, 10, ) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        if( 2 != nFound ) {
          PR_fprintf(PR_STDERR, "Found %lu objects, not 2.\n", nFound);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Found 2 objects: %lu, %lu\n", 
                   found[0], found[1]);

        ck_rv = epv->C_FindObjectsFinal(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        /* Convert a token to session object */

        delta[0].type = CKA_TOKEN;
        delta[0].pValue = &false;
        delta[0].ulValueLen = sizeof(CK_BBOOL);

        ck_rv = epv->C_SetAttributeValue(h, hTSIn, delta, 1);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_SetAttributeValue(%lu, %lu, delta, 1) returned 0x%08x\n", 
                     h, hTSIn, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Changed object from token to session (handle = %lu).\n", hTSIn);

        /* Now find again; there should be one */

        mask[0].type = CKA_APPLICATION;
        mask[0].pValue = key;
        mask[0].ulValueLen = key_len;
        mask[1].type = CKA_TOKEN;
        mask[1].pValue = &true;
        mask[1].ulValueLen = sizeof(CK_BBOOL);

        ck_rv = epv->C_FindObjectsInit(h, mask, 2);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsInit(%lu, mask, 2) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        (void)memset(&found, 0, sizeof(found));
        nFound = 0;
        ck_rv = epv->C_FindObjects(h, found, 10, &nFound);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjects(%lu,, 10, ) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        if( 1 != nFound ) {
          PR_fprintf(PR_STDERR, "Found %lu objects, not 1.\n", nFound);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Found 1 objects: %lu\n", 
                   found[0]);

        ck_rv = epv->C_FindObjectsFinal(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        /* Convert a session to a token object */

        delta[0].type = CKA_TOKEN;
        delta[0].pValue = &true;
        delta[0].ulValueLen = sizeof(CK_BBOOL);

        ck_rv = epv->C_SetAttributeValue(h, hSTIn, delta, 1);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_SetAttributeValue(%lu, %lu, delta, 1) returned 0x%08x\n", 
                     h, hSTIn, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Changed object from session to token (handle = %lu).\n", hSTIn);

        /* Now find again; there should be two again */

        mask[0].type = CKA_APPLICATION;
        mask[0].pValue = key;
        mask[0].ulValueLen = key_len;
        mask[1].type = CKA_TOKEN;
        mask[1].pValue = &true;
        mask[1].ulValueLen = sizeof(CK_BBOOL);

        ck_rv = epv->C_FindObjectsInit(h, mask, 2);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsInit(%lu, mask, 2) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        (void)memset(&found, 0, sizeof(found));
        nFound = 0;
        ck_rv = epv->C_FindObjects(h, found, 10, &nFound);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjects(%lu,, 10, ) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        if( 2 != nFound ) {
          PR_fprintf(PR_STDERR, "Found %lu objects, not 2.\n", nFound);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Found 2 objects: %lu, %lu\n", 
                   found[0], found[1]);

        ck_rv = epv->C_FindObjectsFinal(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        /* Delete the two (found) token objects to clean up */

        ck_rv = epv->C_DestroyObject(h, found[0]);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_DestroyObject(%lu, %lu) returned 0x%08x\n", h, found[0], ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Destroyed token object (handle = %lu)\n", found[0]);

        ck_rv = epv->C_DestroyObject(h, found[1]);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_DestroyObject(%lu, %lu) returned 0x%08x\n", h, found[1], ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Destroyed token object (handle = %lu)\n", found[1]);
        
        /* Close the session and all objects should be gone */

        ck_rv = epv->C_CloseSession(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CloseSession(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }
      } /* if( tinfo.flags & CKF_WRITE_PROTECTED ) */

      if( tinfo.flags & CKF_WRITE_PROTECTED ) {
        PR_fprintf(PR_STDOUT, "Token is write protected, skipping leaving a record.\n");
      } else {
        CK_SESSION_HANDLE h = (CK_SESSION_HANDLE)0;
        CK_ATTRIBUTE record[7], mask[2];
        CK_OBJECT_CLASS cko_data = CKO_DATA;
        CK_BBOOL false = CK_FALSE, true = CK_TRUE;
        char *key = "TEST RECORD";
        CK_ULONG key_len = strlen(key);
        CK_OBJECT_HANDLE hin = (CK_OBJECT_HANDLE)0;
        char timebuffer[256];

        ck_rv = epv->C_OpenSession(pSlots[i], CKF_SERIAL_SESSION, (CK_VOID_PTR)CK_NULL_PTR, (CK_NOTIFY)CK_NULL_PTR, &h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_OpenSession(%lu, CKF_SERIAL_SESSION, , ) returned 0x%08x\n", pSlots[i], ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Opened a session: handle = 0x%08x\n", h);

        /* I can't believe how hard NSPR makes this operation */
        {
          time_t now = 0;
          struct tm *tm;
          time(&now);
          tm = localtime(&now);
          strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %T %Z", tm);
        }

        record[0].type = CKA_CLASS;
        record[0].pValue = &cko_data;
        record[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        record[1].type = CKA_TOKEN;
        record[1].pValue = &true;
        record[1].ulValueLen = sizeof(CK_BBOOL);
        record[2].type = CKA_PRIVATE;
        record[2].pValue = &false;
        record[2].ulValueLen = sizeof(CK_BBOOL);
        record[3].type = CKA_MODIFIABLE;
        record[3].pValue = &true;
        record[3].ulValueLen = sizeof(CK_BBOOL);
        record[4].type = CKA_LABEL;
        record[4].pValue = "Test record";
        record[4].ulValueLen = strlen(record[4].pValue);
        record[5].type = CKA_APPLICATION;
        record[5].pValue = key;
        record[5].ulValueLen = key_len;
        record[6].type = CKA_VALUE;
        record[6].pValue = timebuffer;
        record[6].ulValueLen = strlen(timebuffer)+1;

        PR_fprintf(PR_STDOUT, "    Timestamping with \"%s\"\n", timebuffer);

        ck_rv = epv->C_CreateObject(h, record, 7, &hin);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_CreateObject(%lu, tobj, 7, ) returned 0x%08x\n", h, ck_rv);
          return 1;
        }

        PR_fprintf(PR_STDOUT, "    Created record object: handle = %lu\n", hin);
        
        PR_fprintf(PR_STDOUT, "   == All test timestamps ==\n");

        mask[0].type = CKA_CLASS;
        mask[0].pValue = &cko_data;
        mask[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
        mask[1].type = CKA_APPLICATION;
        mask[1].pValue = key;
        mask[1].ulValueLen = key_len;

        ck_rv = epv->C_FindObjectsInit(h, mask, 2);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsInit(%lu, mask, 1) returned 0x%08x\n", 
                     h, ck_rv);
          return 1;
        }

        while( 1 ) {
          CK_OBJECT_HANDLE o = (CK_OBJECT_HANDLE)0;
          CK_ULONG nObjects = 0;
          CK_ATTRIBUTE value[1];
          char buffer[1024];

          ck_rv = epv->C_FindObjects(h, &o, 1, &nObjects);
          if( CKR_OK != ck_rv ) {
            PR_fprintf(PR_STDERR, "C_FindObjects(%lu, , 1, ) returned 0x%08x\n", h, ck_rv);
            return 1;
          }

          if( 0 == nObjects ) {
            PR_fprintf(PR_STDOUT, "\n");
            break;
          }

          value[0].type = CKA_VALUE;
          value[0].pValue = buffer;
          value[0].ulValueLen = sizeof(buffer);

          ck_rv = epv->C_GetAttributeValue(h, o, value, 1);
          switch( ck_rv ) {
          case CKR_OK:
            PR_fprintf(PR_STDOUT, "    %s\n", value[0].pValue);
            break;
          case CKR_ATTRIBUTE_SENSITIVE:
            PR_fprintf(PR_STDOUT, "    [Sensitive???]\n");
            break;
          case CKR_ATTRIBUTE_TYPE_INVALID:
            PR_fprintf(PR_STDOUT, "    [Invalid attribute???]\n");
            break;
          case CKR_BUFFER_TOO_SMALL:
            PR_fprintf(PR_STDOUT, "    (result > 1k (%lu))\n", value[0].ulValueLen);
            break;
          default:
            PR_fprintf(PR_STDERR, "C_GetAtributeValue(%lu, %lu, CKA_VALUE, 1) returned 0x%08x\n",
                       h, o);
            return 1;
          }
        } /* while */

        ck_rv = epv->C_FindObjectsFinal(h);
        if( CKR_OK != ck_rv ) {
          PR_fprintf(PR_STDERR, "C_FindObjectsFinal(%lu) returned 0x%08x\n", h, ck_rv);
          return 1;
        }
      } /* "leaving a record" else clause */

    }

    PR_fprintf(PR_STDOUT, "\n");
  }

  return 0;
}