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); }