#include "PIVRecord.h"
#include "PIVDefines.h"
#include "PIVError.h"
#include "PIVToken.h"
#include "Attribute.h"
#include "MetaAttribute.h"
#include "MetaRecord.h"
#include <security_cdsa_client/aclclient.h>
#include <Security/SecKey.h>
#include <algorithm>
#include "TLV.h"
#include "PIVUtilities.h"
PIVRecord::~PIVRecord()
{
}
PIVDataRecord::~PIVDataRecord()
{
}
PIVCertificateRecord::~PIVCertificateRecord()
{
}
PIVProtectedRecord::~PIVProtectedRecord()
{
}
void PIVProtectedRecord::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls)
{
if (!mAclEntries) {
mAclEntries.allocator(Allocator::standard());
mAclEntries.add(CssmClient::AclFactory::PinSubject(
mAclEntries.allocator(), 1),
AclAuthorizationSet(CSSM_ACL_AUTHORIZATION_DB_READ, 0));
}
count = mAclEntries.size();
acls = mAclEntries.entries();
}
Tokend::Attribute *PIVDataRecord::getDataAttribute(Tokend::TokenContext *tokenContext)
{
PIVToken &pivToken = dynamic_cast<PIVToken &>(*tokenContext);
if(mAllowCaching && lastAttribute.get())
return lastAttribute.get();
byte_string data;
pivToken.getDataCore(mApplication, description(), mIsCertificate, mAllowCaching, data);
lastAttribute.reset(new Tokend::Attribute(&data[0], data.size()));
return lastAttribute.get();
}
PIVKeyRecord::PIVKeyRecord(const unsigned char *application, size_t applicationSize,
const char *description, const Tokend::MetaRecord &metaRecord,
unsigned char keyRef, size_t keySize) :
PIVRecord(application, applicationSize, description),
keyRef(keyRef), keySize(keySize)
{
attributeAtIndex(metaRecord.metaAttribute(kSecKeyDecrypt).attributeIndex(),
new Tokend::Attribute(true));
attributeAtIndex(metaRecord.metaAttribute(kSecKeyUnwrap).attributeIndex(),
new Tokend::Attribute(true));
attributeAtIndex(metaRecord.metaAttribute(kSecKeySign).attributeIndex(),
new Tokend::Attribute(true));
}
PIVKeyRecord::~PIVKeyRecord()
{
}
size_t PIVKeyRecord::sizeInBits() const {
return keySize;
}
void PIVKeyRecord::computeCrypt(PIVToken &pivToken, bool sign, const AccessCredentials *cred,
const byte_string &data, byte_string &output)
{
if (data.size() != sizeInBits() / 8)
CssmError::throwMe(CSSMERR_CSP_BLOCK_SIZE_MISMATCH);
unsigned char algRef;
switch (sizeInBits()) {
case 1024:
algRef = PIV_KEYALG_RSA_1024;
break;
case 2048:
algRef = PIV_KEYALG_RSA_2048;
break;
default:
CssmError::throwMe(CSSMERR_CSP_KEY_USAGE_INCORRECT);
break;
}
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));
size_t resultLength = sizeInBits() / 8;
resultLength += 1 + TLV::encodedLength(resultLength); resultLength += 1 + 1; resultLength += 1 + TLV::encodedLength(resultLength);
resultLength = resultLength + resultLength % 256 + 256;
output.reserve(resultLength);
PCSC::Transaction _(pivToken);
pivToken.selectDefault();
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)
{
}
else
{
CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
}
}
}
}
byte_string commandString = command->encode();
PIVError::check(pivToken.exchangeChainedAPDU(0x00, 0x87, algRef, keyRef, commandString, output));
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) { 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);
secure_zero(tagData);
}
void PIVKeyRecord::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls)
{
if (!mAclEntries) {
mAclEntries.allocator(Allocator::standard());
mAclEntries.add(CssmClient::AclFactory::AnySubject(
mAclEntries.allocator()),
AclAuthorizationSet(CSSM_ACL_AUTHORIZATION_DB_READ, 0));
CssmData prompt;
char tmptag[20];
const uint32 slot = 1; snprintf(tmptag, sizeof(tmptag), "PIN%d", slot);
if(isUserConsent()) { mAclEntries.add(
CssmClient::AclFactory::PromptPWSubject(mAclEntries.allocator(), prompt),
AclAuthorizationSet(CSSM_ACL_AUTHORIZATION_SIGN, CSSM_ACL_AUTHORIZATION_DECRYPT, 0),
tmptag);
} else {
mAclEntries.add(CssmClient::AclFactory::PinSubject(
mAclEntries.allocator(), 1),
AclAuthorizationSet(CSSM_ACL_AUTHORIZATION_SIGN, CSSM_ACL_AUTHORIZATION_DECRYPT, 0),
tmptag);
}
}
count = mAclEntries.size();
acls = mAclEntries.entries();
}
bool PIVKeyRecord::isUserConsent() const {
return keyRef == PIV_KEYREF_PIV_DIGITAL_SIGNATURE;
}