Example #1
0
void PIVCCC::parse(const byte_string &data) throw(PIVError)
{
	/*
		Sample CCC block
		
		53 44 F0 15 A0 00 00 03 08 01 02 20 50 50 00 11 07 00 00 83 58 00 00 
		83 58 F1 01 21 F2 01 21 F3 00 F4 01 00 F5 01 10 F6 11 00 00 00 00 00 
		00 00 00 00 00 00 00 00 00 00 00 00 F7 00 FA 00 FB 00 FC 00 FD 00 FE 00 90 00
	*/
	// Parse the CCC as a TLV
	TLV_ref tlv;
	try {
		tlv = TLV::parse(data);
	} catch (std::runtime_error &e) {
		PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
	}
	// Check that the return-data tag is correct
	if(tlv->getTag().size() != 1 || tlv->getTag()[0] != PIV_GETDATA_RESPONSE_TAG)
		PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);

	// Iterate over the TLV's contained values to check for desired/invalid values
	TLVList list = tlv->getInnerValues();
	for(TLVList::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
		// No known CCC tags of > 1 byte
		if((*iter)->getTag().size() != 1)
			PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
		uint8_t tag = (*iter)->getTag()[0];
		switch (tag)
		{
		case PIV_CCC_TAG_CARD_IDENTIFIER:			// 0xF0
			// Store the card identifier value persistently
			mIdentifier_content = (*iter)->getValue();
			mIdentifier.Data = &mIdentifier_content[0];
			mIdentifier.Length = mIdentifier_content.size();
			break;
		case PIV_CCC_TAG_CARD_CONTAINER_VERS:		// 0xF1
		case PIV_CCC_TAG_CARD_GRAMMAR_VERS:			// 0xF2
		case PIV_CCC_TAG_APPS_URL:					// 0xF3
		case PIV_CCC_TAG_IS_PKCS15:					// 0xF4
		case PIV_CCC_TAG_DATA_MODEL_NUMBER:			// 0xF5
		case PIV_CCC_TAG_ACL_RULE_TABLE:			// 0xF6
		case PIV_CCC_TAG_CARD_APDUS:				// 0xF7
		case PIV_CCC_TAG_REDIRECTION:				// 0xFA
		case PIV_CCC_TAG_CAPABILITY_TUPLES:			// 0xFB
		case PIV_CCC_TAG_STATUS_TUPLES:				// 0xFC
		case PIV_CCC_TAG_NEXT_CCC:					// 0xFD
		case PIV_CCC_TAG_EXTENDED_APP_URL:			// 0xE3
		case PIV_CCC_TAG_SEC_OBJECT_BUFFER:			// 0xB4
		case PIV_CCC_TAG_ERROR_DETECTION:			// 0xFE
		case 0:
		case 0xFF:
			// Permit these values, but throw them away
			break;
		default:
			// Unknown data is an error condition
			PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
			break;
		}
	}
}
void PIVKeyRecord::computeCrypt(PIVToken &pivToken, bool sign,	// MODIFY
	const AccessCredentials *cred,
	const byte_string &data, byte_string &output)
{
	if (data.size() != sizeInBits() / 8)
		CssmError::throwMe(CSSMERR_CSP_BLOCK_SIZE_MISMATCH);

	/* Allow all key usage, certificates determine validity */
	unsigned char algRef;
	switch (sizeInBits()) {
	case 1024:
		algRef = PIV_KEYALG_RSA_1024;
		break;
	case 2048:
		algRef = PIV_KEYALG_RSA_2048;
		break;
	default:
		/* Cannot use a key ~= 1024 or 2048 bits yet */
		CssmError::throwMe(CSSMERR_CSP_KEY_USAGE_INCORRECT);
		break;
	}

	/* Build the BER-Encoded message */
	/* Template: 0x7C L { 0x82 0x00, 0x81 L data } .. 2 tag+lengths + 1 tag-0 */
	TLVList commandList;
	commandList.push_back(TLV_ref(new TLV(0x81, data)));
	commandList.push_back(TLV_ref(new TLV(0x82)));
	TLV_ref command = TLV_ref(new TLV(0x7C, commandList));

	/* TODO: Evaluate result length handling */
	/* At least enough to contain BER-TLV */
	size_t resultLength = sizeInBits() / 8;
	resultLength += 1 + TLV::encodedLength(resultLength); // RESPONSE
	resultLength += 1 + 1; // Potential empty response-tlv
	resultLength += 1 + TLV::encodedLength(resultLength); // TLV containing response
	/* Round out resultLength to a multiple of 256 */
	resultLength = resultLength + resultLength % 256 + 256;
	// Ensure that there's enough space to prevent unnecessary resizing
	output.reserve(resultLength);

	PCSC::Transaction _(pivToken);
	pivToken.selectDefault();
	/* Support for the signing key w/ user-consent pin */
	if (cred)
	{
		uint32 size = cred->size();
		for (uint32 ix = 0; ix < size; ++ix)
		{
			const TypedList &sample = (*cred)[ix];
			if (sample.type() == CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD
				&& sample.length() == 2)
			{
				CssmData &pin = sample[1].data();
				if (pin.Length > 0)
				{
					pivToken.verifyPIN(1, pin.Data, pin.Length);
					break;
				}
				else if (pin.Length == 0)
				{
					// %%% <rdar://4334623>
					// PIN previously verified by securityd;
					// continue to look at remaining samples
				}
				else
				{
					CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
				}
			}
		}
	}

	byte_string commandString = command->encode();
	PIVError::check(pivToken.exchangeChainedAPDU(0x00, 0x87, algRef, keyRef, commandString, output));

	/* DECODE 0x7C */
	TLV_ref tlv;
	try {
		tlv = TLV::parse(output);
	} catch(...) {
		secure_zero(output);
		PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
	}
	secure_zero(output);
	if(tlv->getTag() != (unsigned char*)"\x7C") {
		secdebug("piv", " %s: computeCrypt: missing response tag: 0x%.2X",
				 description(), 0x7C);
		PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
	}
	byte_string tagData;
	try {
		TLVList list = tlv->getInnerValues();
		TLVList::const_iterator iter = find_if(list.begin(), list.end(), TagPredicate(0x82));
		if(iter != list.end())
			tagData = (*iter)->getValue();
	} catch(...) {
	}
	if(tagData.size() == 0) {
		secdebug("piv", " %s: computeCrypt: missing response value tag: 0x%.2X",
				 description(), 0x82);
		PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
	}

	if(tagData.size() != sizeInBits() / 8) { // Not enough data at all..
		secure_zero(tagData);
		secdebug("piv", " %s: computeCrypt: expected contained response length: %ld, got: %ld",
				 description(), sizeInBits() / 8, tagData.size());
		PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
	}

	output.swap(tagData);
	/* zero-out tagData */
	secure_zero(tagData);
}