Check-in [52569c3e74]
Overview
Comment:Reset the card and rescan for certificates if we get the ISO 7816 error 6D 00 (Wrong instruction) error from the card when trying to verify.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | piv-reset-on-wronginstruction
Files: files | file ages | folders
SHA1: 52569c3e74727533fa081ffc00a7cc827b1226fa
User & Date: rkeene on 2015-07-16 16:06:35
Other Links: branch diff | manifest | tags
Context
2015-07-16
16:18
Updated to retry login if it fails with a 6D 00 (wrong instruction) check-in: 4e30511f94 user: rkeene tags: trunk
16:06
Reset the card and rescan for certificates if we get the ISO 7816 error 6D 00 (Wrong instruction) error from the card when trying to verify. Closed-Leaf check-in: 52569c3e74 user: rkeene tags: piv-reset-on-wronginstruction
2015-07-15
20:05
Added support for updating the PIN check-in: b5ecb7c2d6 user: rkeene tags: trunk
Changes

Modified cackey.c from [3a0d4abad4] to [06b013b460].

3138
3139
3140
3141
3142
3143
3144






































































































3145
3146
3147
3148
3149
3150
3151
 *
 * RETURN VALUE
 *     ...
 *
 * NOTES
 *     ...
 *






































































































 */
static cackey_ret cackey_set_pin(struct cackey_slot *slot, unsigned char *old_pin, unsigned long old_pin_len, unsigned char *pin, unsigned long pin_len) {
	struct cackey_pcsc_identity *pcsc_identities;
	unsigned char cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	unsigned char old_cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	unsigned char pin_update[sizeof(cac_pin) + sizeof(old_cac_pin)];
	unsigned long num_certs;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
 *
 * RETURN VALUE
 *     ...
 *
 * NOTES
 *     ...
 *
 */
static cackey_ret cackey_token_present(struct cackey_slot *slot) {
	cackey_ret pcsc_connect_ret;
	DWORD reader_len = 0, state = 0, protocol = 0, atr_len;
	BYTE atr[MAX_ATR_SIZE];
	LONG status_ret, scard_reconn_ret;

	CACKEY_DEBUG_PRINTF("Called.");

	if (slot->internal) {
		CACKEY_DEBUG_PRINTF("Returning token present (internal token)");

		return(CACKEY_PCSC_S_TOKENPRESENT);
	}

	pcsc_connect_ret = cackey_connect_card(slot);
	if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
		CACKEY_DEBUG_PRINTF("Unable to connect to card, returning token absent");

		return(CACKEY_PCSC_E_TOKENABSENT);
	}

	CACKEY_DEBUG_PRINTF("Calling SCardStatus() to determine card status");

	atr_len = sizeof(atr);
	status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);

	if (status_ret == SCARD_E_INVALID_HANDLE) {
		CACKEY_DEBUG_PRINTF("SCardStatus() returned SCARD_E_INVALID_HANDLE, marking is not already connected and trying again");
		cackey_mark_slot_reset(slot);

		pcsc_connect_ret = cackey_connect_card(slot);
		if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
			CACKEY_DEBUG_PRINTF("Unable to connect to card, returning token absent");

			return(CACKEY_PCSC_E_TOKENABSENT);
		}

		CACKEY_DEBUG_PRINTF("Calling SCardStatus() again");

		atr_len = sizeof(atr);
		status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
	}

	if (status_ret != SCARD_S_SUCCESS) {
		cackey_mark_slot_reset(slot);

		if (status_ret == SCARD_W_RESET_CARD) {
			CACKEY_DEBUG_PRINTF("Reset required, please hold...");

			scard_reconn_ret = cackey_reconnect_card(slot, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1);
			if (scard_reconn_ret == SCARD_S_SUCCESS) {
				/* Re-establish transaction, if it was present */
				if (slot->transaction_depth > 0) {
					slot->transaction_depth--;
					slot->transaction_need_hw_lock = 1;
					cackey_begin_transaction(slot);
				}

				CACKEY_DEBUG_PRINTF("Reset successful, requerying");
				status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
				if (status_ret != SCARD_S_SUCCESS) {
					CACKEY_DEBUG_PRINTF("Still unable to query card status, returning token absent.  SCardStatus() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(status_ret));

					return(CACKEY_PCSC_E_TOKENABSENT);
				}
			} else {
				CACKEY_DEBUG_PRINTF("Unable to reconnect to card, returning token absent.  SCardReconnect() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_reconn_ret));

				return(CACKEY_PCSC_E_TOKENABSENT);
			}
		} else {
			CACKEY_DEBUG_PRINTF("Unable to query card status, returning token absent.  SCardStatus() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(status_ret));

			return(CACKEY_PCSC_E_TOKENABSENT);
		}
	}

	if ((state & SCARD_ABSENT) == SCARD_ABSENT) {
		CACKEY_DEBUG_PRINTF("Card is absent, returning token absent");

		return(CACKEY_PCSC_E_TOKENABSENT);
	}

	CACKEY_DEBUG_PRINTF("Returning token present.");

	return(CACKEY_PCSC_S_TOKENPRESENT);
}

/*
 * SYNPOSIS
 *     ...
 *
 * ARGUMENTS
 *     ...
 *
 * RETURN VALUE
 *     ...
 *
 * NOTES
 *     ...
 *
 */
static cackey_ret cackey_set_pin(struct cackey_slot *slot, unsigned char *old_pin, unsigned long old_pin_len, unsigned char *pin, unsigned long pin_len) {
	struct cackey_pcsc_identity *pcsc_identities;
	unsigned char cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	unsigned char old_cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	unsigned char pin_update[sizeof(cac_pin) + sizeof(old_cac_pin)];
	unsigned long num_certs;
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250

3251
3252
3253
3254
3255
3256
3257
 * RETURN VALUE
 *     ...
 *
 * NOTES
 *     ...
 *
 */
static cackey_ret cackey_login(struct cackey_slot *slot, unsigned char *pin, unsigned long pin_len, int *tries_remaining_p) {
	struct cackey_pcsc_identity *pcsc_identities;
	unsigned char cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	unsigned long num_certs;
	uint16_t response_code;
	int tries_remaining;
	int send_ret;
	int key_reference = 0x00;


	/* Indicate that we do not know about how many tries are remaining */
	if (tries_remaining_p) {
		*tries_remaining_p = -1;
	}

	/* Apparently, CAC PINs are *EXACTLY* 8 bytes long -- pad with 0xFF if too short */







|






|
>







3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
 * RETURN VALUE
 *     ...
 *
 * NOTES
 *     ...
 *
 */
static cackey_ret cackey_login(struct cackey_slot *slot, unsigned char *pin, unsigned long pin_len, int *tries_remaining_p, int retries) {
	struct cackey_pcsc_identity *pcsc_identities;
	unsigned char cac_pin[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	unsigned long num_certs;
	uint16_t response_code;
	int tries_remaining;
	int send_ret;
	int key_reference = 0x00, have_piv = 0;
	cackey_ret connect_ret, token_ret;

	/* Indicate that we do not know about how many tries are remaining */
	if (tries_remaining_p) {
		*tries_remaining_p = -1;
	}

	/* Apparently, CAC PINs are *EXACTLY* 8 bytes long -- pad with 0xFF if too short */
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285




3286
3287
3288
3289
3290
3291
3292
	/* PIV authentication uses a "key_reference" of 0x80 */
	pcsc_identities = cackey_read_certs(slot, NULL, &num_certs);
	if (num_certs > 0 && pcsc_identities != NULL) {
		switch (pcsc_identities[0].id_type) {
			case CACKEY_ID_TYPE_PIV:
				CACKEY_DEBUG_PRINTF("We have PIV card, so we will attempt to authenticate using the PIV Application key reference");

				key_reference = 0x80;
				break;
			default:
				break;
		}

		cackey_free_certs(pcsc_identities, num_certs, 1);
	}





	/* Issue PIN Verify */
	send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_VERIFY, 0x00, key_reference, sizeof(cac_pin), cac_pin, 0x00, &response_code, NULL, NULL);

	if (send_ret != CACKEY_PCSC_S_OK) {
		if ((response_code & 0x63C0) == 0x63C0) {
			tries_remaining = (response_code & 0xF);







|







>
>
>
>







3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
	/* PIV authentication uses a "key_reference" of 0x80 */
	pcsc_identities = cackey_read_certs(slot, NULL, &num_certs);
	if (num_certs > 0 && pcsc_identities != NULL) {
		switch (pcsc_identities[0].id_type) {
			case CACKEY_ID_TYPE_PIV:
				CACKEY_DEBUG_PRINTF("We have PIV card, so we will attempt to authenticate using the PIV Application key reference");

				have_piv = 1;
				break;
			default:
				break;
		}

		cackey_free_certs(pcsc_identities, num_certs, 1);
	}

	if (have_piv == 1) {
		key_reference = 0x80;
	}

	/* Issue PIN Verify */
	send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_VERIFY, 0x00, key_reference, sizeof(cac_pin), cac_pin, 0x00, &response_code, NULL, NULL);

	if (send_ret != CACKEY_PCSC_S_OK) {
		if ((response_code & 0x63C0) == 0x63C0) {
			tries_remaining = (response_code & 0xF);
3301
3302
3303
3304
3305
3306
3307





























3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
		}

		if (response_code == 0x6983) {
			CACKEY_DEBUG_PRINTF("PIN Verification failed, device is locked");

			return(CACKEY_PCSC_E_LOCKED);
		}






























		return(CACKEY_PCSC_E_GENERIC);
	}

	CACKEY_DEBUG_PRINTF("PIN Verification succeeded");

	return(CACKEY_PCSC_S_OK);
}

/*
 * SYNPOSIS
 *     ...
 *
 * ARGUMENTS
 *     ...
 *
 * RETURN VALUE
 *     ...
 *
 * NOTES
 *     ...
 *
 */
static cackey_ret cackey_token_present(struct cackey_slot *slot) {
	cackey_ret pcsc_connect_ret;
	DWORD reader_len = 0, state = 0, protocol = 0, atr_len;
	BYTE atr[MAX_ATR_SIZE];
	LONG status_ret, scard_reconn_ret;

	CACKEY_DEBUG_PRINTF("Called.");

	if (slot->internal) {
		CACKEY_DEBUG_PRINTF("Returning token present (internal token)");

		return(CACKEY_PCSC_S_TOKENPRESENT);
	}

	pcsc_connect_ret = cackey_connect_card(slot);
	if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
		CACKEY_DEBUG_PRINTF("Unable to connect to card, returning token absent");

		return(CACKEY_PCSC_E_TOKENABSENT);
	}

	CACKEY_DEBUG_PRINTF("Calling SCardStatus() to determine card status");

	atr_len = sizeof(atr);
	status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);

	if (status_ret == SCARD_E_INVALID_HANDLE) {
		CACKEY_DEBUG_PRINTF("SCardStatus() returned SCARD_E_INVALID_HANDLE, marking is not already connected and trying again");
		cackey_mark_slot_reset(slot);

		pcsc_connect_ret = cackey_connect_card(slot);
		if (pcsc_connect_ret != CACKEY_PCSC_S_OK) {
			CACKEY_DEBUG_PRINTF("Unable to connect to card, returning token absent");

			return(CACKEY_PCSC_E_TOKENABSENT);
		}

		CACKEY_DEBUG_PRINTF("Calling SCardStatus() again");

		atr_len = sizeof(atr);
		status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
	}

	if (status_ret != SCARD_S_SUCCESS) {
		cackey_mark_slot_reset(slot);

		if (status_ret == SCARD_W_RESET_CARD) {
			CACKEY_DEBUG_PRINTF("Reset required, please hold...");

			scard_reconn_ret = cackey_reconnect_card(slot, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1);
			if (scard_reconn_ret == SCARD_S_SUCCESS) {
				/* Re-establish transaction, if it was present */
				if (slot->transaction_depth > 0) {
					slot->transaction_depth--;
					slot->transaction_need_hw_lock = 1;
					cackey_begin_transaction(slot);
				}

				CACKEY_DEBUG_PRINTF("Reset successful, requerying");
				status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len);
				if (status_ret != SCARD_S_SUCCESS) {
					CACKEY_DEBUG_PRINTF("Still unable to query card status, returning token absent.  SCardStatus() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(status_ret));

					return(CACKEY_PCSC_E_TOKENABSENT);
				}
			} else {
				CACKEY_DEBUG_PRINTF("Unable to reconnect to card, returning token absent.  SCardReconnect() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_reconn_ret));

				return(CACKEY_PCSC_E_TOKENABSENT);
			}
		} else {
			CACKEY_DEBUG_PRINTF("Unable to query card status, returning token absent.  SCardStatus() = %s", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(status_ret));

			return(CACKEY_PCSC_E_TOKENABSENT);
		}
	}

	if ((state & SCARD_ABSENT) == SCARD_ABSENT) {
		CACKEY_DEBUG_PRINTF("Card is absent, returning token absent");

		return(CACKEY_PCSC_E_TOKENABSENT);
	}

	CACKEY_DEBUG_PRINTF("Returning token present.");

	return(CACKEY_PCSC_S_TOKENPRESENT);
}

/*
 * SYNPOSIS
 *     ...
 *
 * ARGUMENTS
 *     ...
 *







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452






































































































3453
3454
3455
3456
3457
3458
3459
		}

		if (response_code == 0x6983) {
			CACKEY_DEBUG_PRINTF("PIN Verification failed, device is locked");

			return(CACKEY_PCSC_E_LOCKED);
		}

		if (response_code == 0x6d00) {
			if (have_piv == 1 && retries > 0) {
				CACKEY_DEBUG_PRINTF("Got ISO 7816 Response \"6D 00\" in response to a VERIFY request.");
				CACKEY_DEBUG_PRINTF("We did not expect this because it is not mentioned in NIST SP 800-73-3 Part 2 Section 3.2.1");
				CACKEY_DEBUG_PRINTF("We are going to try to reset the card and select the applet again.");

				cackey_mark_slot_reset(slot);

				connect_ret = cackey_connect_card(slot);
				if (connect_ret != CACKEY_PCSC_S_OK) {
					CACKEY_DEBUG_PRINTF("Unable to reconnect after resetting the card, returning in error.");

					return(connect_ret);
				}

				CACKEY_DEBUG_PRINTF("Verifying we still have a token.");
				token_ret = cackey_token_present(slot);
				if (token_ret != CACKEY_PCSC_S_TOKENPRESENT) {
					CACKEY_DEBUG_PRINTF("Token not present, returning in error.");

					return(token_ret);
				}


				CACKEY_DEBUG_PRINTF("Trying to login again");
				return(cackey_login(slot, pin, pin_len, tries_remaining_p, retries - 1));
			}
		}

		return(CACKEY_PCSC_E_GENERIC);
	}

	CACKEY_DEBUG_PRINTF("PIN Verification succeeded");

	return(CACKEY_PCSC_S_OK);
}







































































































/*
 * SYNPOSIS
 *     ...
 *
 * ARGUMENTS
 *     ...
 *
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
			return(CKR_PIN_INCORRECT);
		}

		pPin = (CK_UTF8CHAR_PTR) pinbuf;
		ulPinLen = strlen(pinbuf);
	}

	login_ret = cackey_login(&cackey_slots[slotID], pPin, ulPinLen, &tries_remaining);
	if (login_ret != CACKEY_PCSC_S_OK) {
		if (lock_mutex) {
			cackey_mutex_unlock(cackey_biglock);
		}

		if (login_ret == CACKEY_PCSC_E_LOCKED) {
			CACKEY_DEBUG_PRINTF("Error.  Token is locked.");







|







5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
			return(CKR_PIN_INCORRECT);
		}

		pPin = (CK_UTF8CHAR_PTR) pinbuf;
		ulPinLen = strlen(pinbuf);
	}

	login_ret = cackey_login(&cackey_slots[slotID], pPin, ulPinLen, &tries_remaining, 3);
	if (login_ret != CACKEY_PCSC_S_OK) {
		if (lock_mutex) {
			cackey_mutex_unlock(cackey_biglock);
		}

		if (login_ret == CACKEY_PCSC_E_LOCKED) {
			CACKEY_DEBUG_PRINTF("Error.  Token is locked.");