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); }
byte_string_ref (byte_string & bs) : p(& bs) , max_size(bs.size()) {}