#include "PIVToken.h"
#include "PIVDefines.h"
#include "PIVCCC.h"
#include "Adornment.h"
#include "AttributeCoder.h"
#include "PIVError.h"
#include "PIVRecord.h"
#include "PIVSchema.h"
#include <security_cdsa_client/aclclient.h>
#include <map>
#include <vector>
#include <zlib.h>
#include <CoreFoundation/CFString.h>
#include <Security/Security.h>
#include <algorithm>
#include "TLV.h"
using CssmClient::AclFactory;
#pragma mark ---------- PIV defines ----------
#define PIV_RESULT_SUCCESS_SW1 0x90 //[ref SCARD_SUCCESS]
#define PIV_RESULT_SUCCESS_SW2 (unsigned char )0x00
#define PIV_RESULT_CONTINUATION_SW1 (unsigned char )0x61
static const unsigned char kSelectPIVApplet[] = { SELECT_PIV_APPLET_LONG };
static const unsigned char kUniversalAID[] = { 0xA0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00 };
#pragma mark ---------- Data Description Strings -----------
static const char *sDescripCardCapabilityContainer = "CCC";
static const char *sDescripCardHolderUniqueIdentifier = "CHUID";
static const char *sDescripCardHolderFingerprints = "FINGERPRINTS";
static const char *sDescripPrintedInformation = "PRINTDATA";
static const char *sDescripCardHolderFacialImage = "FACIALIMAGE";
#pragma mark ---------- Object IDs ----------
static const unsigned char oidCardCapabilityContainer[] = { PIV_OBJECT_ID_CARD_CAPABILITY_CONTAINER };
static const unsigned char oidCardHolderUniqueIdentifier[] = { PIV_OBJECT_ID_CARDHOLDER_UNIQUEID };
static const unsigned char oidCardHolderFingerprints[] = { PIV_OBJECT_ID_CARDHOLDER_FINGERPRINTS };
static const unsigned char oidPrintedInformation[] = { PIV_OBJECT_ID_PRINTED_INFORMATION };
static const unsigned char oidCardHolderFacialImage[] = { PIV_OBJECT_ID_CARDHOLDER_FACIAL_IMAGE };
static const unsigned char oidX509CertificatePIVAuthentication[] = { PIV_OBJECT_ID_X509_CERTIFICATE_PIV_AUTHENTICATION };
static const unsigned char oidX509CertificateDigitalSignature[] = { PIV_OBJECT_ID_X509_CERTIFICATE_DIGITAL_SIGNATURE };
static const unsigned char oidX509CertificateKeyManagement[] = { PIV_OBJECT_ID_X509_CERTIFICATE_KEY_MANAGEMENT };
static const unsigned char oidX509CertificateCardAuthentication[] = { PIV_OBJECT_ID_X509_CERTIFICATE_CARD_AUTHENTICATION };
#pragma mark ---------- NO/MINOR MODIFICATION NEEDED ----------
PIVToken::PIVToken() :
mCurrentApplet(NULL), mPinStatus(0)
{
mTokenContext = this;
mSession.open();
}
PIVToken::~PIVToken()
{
delete mSchema;
}
void PIVToken::didDisconnect()
{
PCSC::Card::didDisconnect();
mCurrentApplet = NULL;
mPinStatus = 0;
}
void PIVToken::didEnd()
{
PCSC::Card::didEnd();
mCurrentApplet = NULL;
mPinStatus = 0;
}
void PIVToken::unverifyPIN(int pinNum)
{
if (pinNum != -1)
CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
end(SCARD_RESET_CARD);
}
void PIVToken::establish(const CSSM_GUID *guid, uint32 subserviceId,
SecTokendEstablishFlags flags, const char *cacheDirectory,
const char *workDirectory, char mdsDirectory[PATH_MAX],
char printName[PATH_MAX])
{
Tokend::ISO7816Token::establish(guid, subserviceId, flags,
cacheDirectory, workDirectory, mdsDirectory, printName);
#ifdef _USECERTIFICATECOMMONNAME
std::string commonName = authCertCommonName();
::snprintf(printName, 40, "PIV-%s", commonName.c_str());
#else
byte_string cccOid((const unsigned char *)oidCardCapabilityContainer, oidCardCapabilityContainer + sizeof(oidCardCapabilityContainer));
byte_string cccdata;
getDataCore(cccOid, "CCC", false, true, cccdata);
PIVCCC ccc(cccdata);
::snprintf(printName, 40, "PIV-%s", ccc.hexidentifier().c_str());
#endif
Tokend::ISO7816Token::name(printName);
secdebug("pivtoken", "name: %s", printName);
if(mSchema)
delete mSchema;
mSchema = new PIVSchema();
mSchema->create();
populate();
}
void PIVToken::getOwner(AclOwnerPrototype &owner)
{
if (!mAclOwner)
{
mAclOwner.allocator(Allocator::standard());
mAclOwner = AclFactory::PinSubject(Allocator::standard(), 0);
}
owner = mAclOwner;
}
void PIVToken::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls)
{
Allocator &alloc = Allocator::standard();
if (unsigned pin = pinFromAclTag(tag, "?")) {
static AutoAclEntryInfoList acl;
acl.clear();
acl.allocator(alloc);
uint32_t status = this->pinStatus(pin);
if (status == SCARD_SUCCESS)
acl.addPinState(pin, CSSM_ACL_PREAUTH_TRACKING_AUTHORIZED);
else if (status >= PIV_AUTHENTICATION_FAILED_0 && status <= PIV_AUTHENTICATION_FAILED_3)
acl.addPinState(pin, 0, status - PIV_AUTHENTICATION_FAILED_0);
else
acl.addPinState(pin, CSSM_ACL_PREAUTH_TRACKING_UNKNOWN);
count = acl.size();
acls = acl.entries();
return;
}
if (!mAclEntries) {
mAclEntries.allocator(alloc);
mAclEntries.add(CssmClient::AclFactory::AnySubject(
mAclEntries.allocator()),
AclAuthorizationSet(CSSM_ACL_AUTHORIZATION_DB_READ, 0));
mAclEntries.addPin(AclFactory::PWSubject(alloc), 1);
mAclEntries.addPin(AclFactory::PromptPWSubject(alloc, CssmData()), 1);
}
count = mAclEntries.size();
acls = mAclEntries.entries();
}
#pragma mark ---------- MODIFICATION REQUIRED ----------
uint32 PIVToken::probe(SecTokendProbeFlags flags, char tokenUid[TOKEND_MAX_UID]) {
uint32 score = Tokend::ISO7816Token::probe(flags, tokenUid);
bool doDisconnect = false;
try
{
if (!identify())
doDisconnect = true;
else
{
#ifndef _USEFALLBACKTOKENUID
byte_string cccOid((const unsigned char *)oidCardCapabilityContainer, oidCardCapabilityContainer + sizeof(oidCardCapabilityContainer));
byte_string cccdata;
const bool allowCaching = false;
getDataCore(cccOid, "CCC", false, allowCaching, cccdata);
PIVCCC ccc(cccdata);
snprintf(tokenUid, TOKEND_MAX_UID, "PIV-%s", ccc.hexidentifier().c_str());
#else
unsigned char buffer[80];
time_t now;
struct tm* timestruct = localtime(&now);
strftime(reinterpret_cast<char *>(buffer), 80, "%+", timestruct); snprintf(tokenUid, TOKEND_MAX_UID, "PIV-%s", buffer);
#endif
score = 110;
secdebug("probe", "recognized %s", tokenUid);
}
}
catch (...)
{
doDisconnect = true;
score = 0;
}
if (doDisconnect)
disconnect();
return score;
}
size_t PIVToken::getKeySize(const byte_string &cert) const {
size_t keySize = 0;
SecCertificateRef certRef = 0;
SecKeyRef keyRef = 0;
CSSM_DATA certData;
certData.Data = (uint8_t*)&cert[0];
certData.Length = cert.size();
const CSSM_KEY *cssmKey = NULL;
OSStatus status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &certRef);
if(status != noErr) goto done;
status = SecCertificateCopyPublicKey(certRef, &keyRef);
if(status != noErr) goto done;
status = SecKeyGetCSSMKey(keyRef, &cssmKey);
if(status != noErr) goto done;
keySize = cssmKey->KeyHeader.LogicalKeySizeInBits;
done:
if(keyRef)
CFRelease(keyRef);
if(certRef)
CFRelease(certRef);
return keySize;
}
void PIVToken::populate()
{
secdebug("populate", "PIVToken::populate() begin");
Tokend::Relation &certRelation =
mSchema->findRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE);
Tokend::Relation &privateKeyRelation =
mSchema->findRelation(CSSM_DL_DB_RECORD_PRIVATE_KEY);
Tokend::Relation &dataRelation =
mSchema->findRelation(CSSM_DL_DB_RECORD_GENERIC);
const size_t sz = sizeof(oidCardCapabilityContainer);
if (getDataExists(oidCardCapabilityContainer, sz, sDescripCardCapabilityContainer))
dataRelation.insertRecord(new PIVDataRecord(oidCardCapabilityContainer, sz, sDescripCardCapabilityContainer));
if (getDataExists(oidCardHolderUniqueIdentifier, sz, sDescripCardHolderUniqueIdentifier))
dataRelation.insertRecord(new PIVDataRecord(oidCardHolderUniqueIdentifier, sz, sDescripCardHolderUniqueIdentifier));
if (getDataExists(oidCardHolderFingerprints, sz, sDescripCardHolderFingerprints))
dataRelation.insertRecord(new PIVProtectedRecord(oidCardHolderFingerprints, sz, sDescripCardHolderFingerprints));
if (getDataExists(oidPrintedInformation, sz, sDescripPrintedInformation))
dataRelation.insertRecord(new PIVProtectedRecord(oidPrintedInformation, sz, sDescripPrintedInformation));
if (getDataExists(oidCardHolderFacialImage, sz, sDescripCardHolderFacialImage))
dataRelation.insertRecord(new PIVProtectedRecord(oidCardHolderFacialImage, sz, sDescripCardHolderFacialImage));
const unsigned char *certids[] =
{
oidX509CertificatePIVAuthentication, oidX509CertificateDigitalSignature, oidX509CertificateKeyManagement, oidX509CertificateCardAuthentication };
const char *certNames[] =
{
"PIV Authentication Certificate",
"Digital Signature Certificate",
"Key Management Certificate",
"Card Authentication Certificate"
};
const char *keyNames[] =
{
"PIV Authentication Private Key", "Digital Signature Private Key", "Key Management Private Key", "Card Authentication Private Key" };
const unsigned char keyRefs[] =
{
PIV_KEYREF_PIV_AUTHENTICATION,
PIV_KEYREF_PIV_DIGITAL_SIGNATURE,
PIV_KEYREF_PIV_KEY_MANAGEMENT,
PIV_KEYREF_PIV_CARD_AUTHENTICATION
};
for (unsigned int ix=0;ix<sizeof(certids)/sizeof(certids[0]);++ix)
{
byte_string certData;
try {
getDataCore(byte_string(certids[ix], certids[ix] + sz), certNames[ix], true, true, certData);
} catch(PIVError &e) {
continue;
}
int keySize = getKeySize(certData);
if(keySize == 0) continue;
RefPointer<Tokend::Record> cert(new PIVCertificateRecord(certids[ix], sz, certNames[ix]));
certRelation.insertRecord(cert);
RefPointer<Tokend::Record> key(new PIVKeyRecord(certids[ix], sz, keyNames[ix], privateKeyRelation.metaRecord(), keyRefs[ix], keySize));
privateKeyRelation.insertRecord(key);
key->setAdornment(mSchema->publicKeyHashCoder().certificateKey(),
new Tokend::LinkedRecordAdornment(cert));
}
secdebug("populate", "PIVToken::populate() end");
}
bool PIVToken::identify()
{
try
{
selectDefault();
return true;
}
catch (const PCSC::Error &error)
{
if (error.error == SCARD_E_PROTO_MISMATCH)
return false;
throw;
}
}
void PIVToken::changePIN(int pinNum,
const unsigned char *oldPin, size_t oldPinLength,
const unsigned char *newPin, size_t newPinLength)
{
if (pinNum < PIV_VERIFY_KEY_NUMBER_DEFAULT || pinNum > PIV_VERIFY_KEY_NUMBER_MAX)
CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
if (oldPinLength < PIV_VERIFY_PIN_LENGTH_MIN || oldPinLength > PIV_VERIFY_PIN_LENGTH_MAX ||
newPinLength < PIV_VERIFY_PIN_LENGTH_MIN || newPinLength > PIV_VERIFY_PIN_LENGTH_MAX)
CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
PCSC::Transaction _(*this);
selectDefault();
const unsigned char dataFieldLen = 0x10; const unsigned char APDU_TEMPLATE[] = { PIV_CHANGE_REFERENCE_DATA_APDU_TEMPLATE };
byte_string apdu(APDU_TEMPLATE, APDU_TEMPLATE + sizeof(APDU_TEMPLATE));
apdu[PIV_VERIFY_APDU_INDEX_KEY] = static_cast<unsigned char>(pinNum & 0xFF);
apdu[PIV_VERIFY_APDU_INDEX_LEN] = dataFieldLen;
copy(oldPin, oldPin + oldPinLength, apdu.begin() + PIV_VERIFY_APDU_INDEX_DATA);
copy(newPin, newPin + newPinLength, apdu.begin() + PIV_CHANGE_REFERENCE_DATA_APDU_INDEX_DATA2);
byte_string result;
mPinStatus = exchangeAPDU(apdu, result);
secure_zero(apdu);
PIVError::check(mPinStatus);
}
uint32_t PIVToken::pinStatus(int pinNum)
{
if (pinNum < PIV_VERIFY_KEY_NUMBER_DEFAULT || pinNum > PIV_VERIFY_KEY_NUMBER_MAX)
CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
if (mPinStatus && isInTransaction())
return mPinStatus;
PCSC::Transaction _(*this);
selectDefault();
const unsigned char APDU_TEMPLATE[] = { PIV_VERIFY_APDU_STATUS };
byte_string apdu(APDU_TEMPLATE, APDU_TEMPLATE + sizeof(APDU_TEMPLATE));
apdu[PIV_VERIFY_APDU_INDEX_KEY] = 0x80;
byte_string result;
mPinStatus = exchangeAPDU(apdu, result);
if (((mPinStatus & 0xFF00) != SCARD_AUTHENTICATION_FAILED) &&
(mPinStatus != SCARD_AUTHENTICATION_BLOCKED))
PIVError::check(mPinStatus);
if ((mPinStatus & 0xFF00) == SCARD_AUTHENTICATION_FAILED)
secdebug("pivtoken", "pinStatus: %d authentication attempts remaining", (mPinStatus & 0x000F));
else
if (mPinStatus == SCARD_AUTHENTICATION_BLOCKED)
secdebug("pivtoken", "pinStatus: CARD IS BLOCKED");
return mPinStatus;
}
void PIVToken::verifyPIN(int pinNum,
const unsigned char *pin, size_t pinLength)
{
if (pinNum < PIV_VERIFY_KEY_NUMBER_DEFAULT || pinNum > PIV_VERIFY_KEY_NUMBER_MAX)
CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
if (pinLength < PIV_VERIFY_PIN_LENGTH_MIN || pinLength > PIV_VERIFY_PIN_LENGTH_MAX)
CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
PCSC::Transaction _(*this);
selectDefault();
const unsigned char dataFieldLen = 8;
const unsigned char APDU_TEMPLATE[] = { PIV_VERIFY_APDU_TEMPLATE };
byte_string apdu(APDU_TEMPLATE, APDU_TEMPLATE + sizeof(APDU_TEMPLATE));
apdu[PIV_VERIFY_APDU_INDEX_KEY] = 0x80; apdu[PIV_VERIFY_APDU_INDEX_LEN] = dataFieldLen;
copy(pin, pin + pinLength, apdu.begin() + PIV_VERIFY_APDU_INDEX_DATA);
byte_string result;
mPinStatus = exchangeAPDU(apdu, result);
secure_zero(apdu);
PIVError::check(mPinStatus);
begin();
}
#pragma mark ---------------- TOKEN Specific/Utility --------------
void PIVToken::select(const unsigned char *applet, size_t appletLength)
{
secdebug("pivtoken", "select BEGIN");
if (isInTransaction() && mCurrentApplet == applet)
return;
byte_string apdu(applet, applet + appletLength);
byte_string result;
bool failed = false;
uint16_t rx;
try
{
rx = exchangeAPDU(apdu, result);
}
catch (const PCSC::Error &error)
{
secdebug("pivtoken", "select transmit error: %ld (0x%04lX)]", error.error, error.error);
if (error.error == SCARD_E_PROTO_MISMATCH)
return;
failed = true;
}
catch (...)
{
secdebug("pivtoken", "select transmit unknown failure");
failed = true;
}
if (failed || (rx != SCARD_SUCCESS))
{
secdebug("pivtoken", "select END [FAILURE %02X %02X]",
result[result.size() - 2], result[result.size() - 1]);
PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
}
if (isInTransaction())
mCurrentApplet = applet;
secdebug("pivtoken", "select END [SUCCESS]");
}
void PIVToken::selectDefault()
{
select(kSelectPIVApplet, sizeof(kSelectPIVApplet));
}
uint16_t PIVToken::simpleExchangeAPDU(const byte_string &apdu, byte_string &result) {
transmit(apdu, result);
if (result.size() < 2)
PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
uint16_t ret = (result[result.size() - 2] << 8) + result[result.size() - 1];
result.resize(result.size() - 2);
return ret;
}
uint16_t PIVToken::exchangeAPDU(const byte_string &apdu, byte_string &result)
{
static const uint8_t GET_RESULT_TEMPLATE [] = { 0x00, 0xC0, 0x00, 0x00, 0xFF };
byte_string getResult(GET_RESULT_TEMPLATE, GET_RESULT_TEMPLATE + sizeof(GET_RESULT_TEMPLATE));
const int SIZE_INDEX = 4;
uint16_t ret = simpleExchangeAPDU(apdu, result);
while ((ret >> 8) == PIV_RESULT_CONTINUATION_SW1)
{
size_t expectedLength = ret & 0xFF;
if(expectedLength == 0)
expectedLength = 256;
getResult[SIZE_INDEX] = expectedLength & 0xFF;
ret = simpleExchangeAPDU(getResult, result);
}
return ret;
}
uint16_t PIVToken::exchangeChainedAPDU(unsigned char cla, unsigned char ins,
unsigned char p1, unsigned char p2,
const byte_string &data,
byte_string &result)
{
const size_t BASE_CHUNK_LENGTH = 242;
byte_string apdu;
uint16_t ret;
apdu.reserve(5 + BASE_CHUNK_LENGTH);
apdu.resize(5);
apdu[0] = cla;
apdu[1] = ins;
apdu[2] = p1;
apdu[3] = p2;
apdu[0] |= 0x10;
byte_string::iterator apduDataBegin = apdu.begin() + 5;
size_t chunkLength;
byte_string::const_iterator iter;
for(iter = data.begin(); (iter + BASE_CHUNK_LENGTH) < data.end(); iter += BASE_CHUNK_LENGTH) {
chunkLength = std::min(BASE_CHUNK_LENGTH, (size_t)(data.end() - iter));
apdu.resize(5 + chunkLength);
apdu[4] = chunkLength & 0xFF;
copy(iter, iter + chunkLength, apduDataBegin);
ret = simpleExchangeAPDU(apdu, result);
PIVError::check(ret);
}
apdu[0] &= ~0x10;
apdu[4] = (data.end() - iter) & 0xFF;
apdu.resize(5 + (data.end() - iter));
copy(iter, data.end(), apduDataBegin);
return exchangeAPDU(apdu, result);
}
byte_string PIVToken::buildGetData(const byte_string &oid, int limit ) const {
if (oid.size() != 3)
PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
const unsigned char dataFieldLen = 0x05;
static const unsigned char INITIAL_APDU_TEMPLATE[] = { PIV_GETDATA_APDU_TEMPLATE };
byte_string initialApdu(INITIAL_APDU_TEMPLATE, INITIAL_APDU_TEMPLATE + sizeof(INITIAL_APDU_TEMPLATE));
initialApdu[PIV_GETDATA_APDU_INDEX_LEN] = dataFieldLen;
initialApdu[PIV_GETDATA_APDU_INDEX_OIDLEN] = oid.size();
copy(oid.begin(), oid.end(), initialApdu.begin() + PIV_GETDATA_APDU_INDEX_OID);
initialApdu.resize(PIV_GETDATA_APDU_INDEX_OID + oid.size());
if(limit > 255)
PCSC::Error::throwMe(SCARD_E_PROTO_MISMATCH);
if(limit >= 0)
initialApdu.push_back(limit);
return initialApdu;
}
void PIVToken::getDataCore(const byte_string &oid, const char *description, bool isCertificate,
bool allowCaching, byte_string &data)
{
CssmData cssmData;
if(allowCaching && cachedObject(0, description, cssmData)) {
data.assign(cssmData.Data, cssmData.Data + cssmData.Length);
free(cssmData.Data);
return;
}
{
byte_string getDataApdu = buildGetData(oid);
PCSC::Transaction _(*this);
selectDefault();
uint16_t rx = exchangeAPDU(getDataApdu, data);
secdebug("pivtokend", "exchangeAPDU result %02X", rx);
PIVError::check(rx);
if(data.size() > PIV_MAX_DATA_SIZE) {
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
}
}
dumpDataRecord(data, oid);
if (data.size()<=0)
return;
if (data[0] != PIV_GETDATA_RESPONSE_TAG)
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
if (isCertificate)
processCertificateRecord(data, oid, description);
if (!allowCaching)
return;
cssmData.Data = &data[0];
cssmData.Length = data.size();
cacheObject(0, description, cssmData);
}
void PIVToken::processCertificateRecord(byte_string &data, const byte_string &oid, const char *description)
{
bool hasCertificateData = false;
bool isCompressed = false;
TLV_ref tlv;
TLVList list;
try {
tlv = TLV::parse(data);
list = tlv->getInnerValues();
} catch(...) {
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
}
for(TLVList::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
const byte_string &tagString = (*iter)->getTag();
const byte_string &value = (*iter)->getValue();
if(tagString.size() != 1)
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
uint8_t tag = tagString[0];
switch (tag)
{
case PIV_GETDATA_TAG_CERTIFICATE: data = value;
hasCertificateData = true;
break;
case PIV_GETDATA_TAG_CERTINFO: if(value.size() != 1)
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
secdebug("pivtokend", "CertInfo byte: %02X", value[0]);
isCompressed = value[0] & PIV_GETDATA_COMPRESSION_MASK;
break;
case PIV_GETDATA_TAG_MSCUID: break;
case PIV_GETDATA_TAG_ERRORDETECTION:
break;
case 0:
case 0xFF:
break;
default:
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
break;
}
}
if(!hasCertificateData)
PIVError::throwMe(SCARD_RETURNED_DATA_CORRUPTED);
if (isCompressed)
{
secdebug("pivtokend", "uncompressing compressed %s", description);
dumpDataRecord(data, oid, "-compressedcert");
byte_string uncompressedData;
uncompressedData.resize(PIV_MAX_DATA_SIZE);
int rv = Z_ERRNO;
int compTyp = compressionType(data);
rv = PIVToken::uncompressData(uncompressedData, data, compTyp);
if (rv != Z_OK)
{
secdebug("zlib", "uncompressing %s failed: %d [type=%d]", description, rv, compTyp);
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
}
data = uncompressedData;
}
else
{
}
dumpDataRecord(data, oid, "-rawcert");
}
int PIVToken::compressionType(const byte_string &data)
{
if (data.size() > 2 && data[0] == 0x1F && data[1] == 0x8B)
return kCompressionGzip;
if (data.size() > 1 )
return kCompressionZlib;
else
return kCompressionUnknown;
}
int PIVToken::uncompressData(byte_string &uncompressedData, const byte_string &compressedData, int compressionType)
{
z_stream dstream; int windowSize = 15;
switch(compressionType) {
case kCompressionGzip:
windowSize += 0x20;
break;
case kCompressionZlib:
break;
default:
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
}
dstream.zalloc = (alloc_func)0;
dstream.zfree = (free_func)0;
dstream.opaque = (voidpf)0;
dstream.next_in = (Bytef*)&compressedData[0];
dstream.avail_in = compressedData.size();
dstream.next_out = &uncompressedData[0];
dstream.avail_out = uncompressedData.size();
int err = inflateInit2(&dstream, windowSize);
if (err)
return err;
err = inflate(&dstream, Z_FINISH);
if (err != Z_STREAM_END)
{
inflateEnd(&dstream);
return err;
}
uncompressedData.resize(dstream.total_out);
err = inflateEnd(&dstream);
return err;
}
void PIVToken::dumpDataRecord(const byte_string &data, const byte_string &oid, const char *extraSuffix)
{
#if !defined(NDEBUG)
FILE *fp;
char fileName[128]={0,};
const char *kNamePrefix = "/tmp/pivobj-";
char suffix[32]={0,};
memcpy(fileName, kNamePrefix, strlen(kNamePrefix));
sprintf(suffix,"%02X%02X%02X", oid[0], oid[1], oid[2]);
strncat(fileName, suffix, 3);
if (extraSuffix)
strcat(fileName, extraSuffix);
if ((fp = fopen(fileName, "wb")) != NULL)
{
fwrite(&data[0], 1, data.size(), fp);
fclose(fp);
secdebug("pivtokend", "wrote data of length %ld to %s", data.size(), fileName);
}
#endif
}
std::string PIVToken::authCertCommonName()
{
const char *cn = NULL;
SecCertificateRef certificateRef = NULL;
CFStringRef commonName = NULL;
byte_string data;
byte_string oidAuthCert(oidX509CertificatePIVAuthentication, oidX509CertificatePIVAuthentication + sizeof(oidX509CertificatePIVAuthentication));
getDataCore(oidAuthCert, "AUTHCERT", true, true, data);
CssmData certData(&data[0], data.size());
OSStatus status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, &certificateRef);
if (!status)
{
CFStringRef commonName = NULL;
SecCertificateCopyCommonName(certificateRef, &commonName);
if (commonName)
cn = CFStringGetCStringPtr(commonName, kCFStringEncodingMacRoman);
}
if (certificateRef)
CFRelease(certificateRef);
if (commonName)
CFRelease(commonName);
return std::string(cn?cn:"--unknown--");
}
size_t PIVToken::transmit(const byte_string::const_iterator &apduBegin, const byte_string::const_iterator &apduEnd, byte_string &result) {
const size_t BUFFER_SIZE = 1024;
size_t resultLength = BUFFER_SIZE;
size_t index = result.size();
secure_resize(result, result.size() + BUFFER_SIZE);
ISO7816Token::transmit(&(*apduBegin), (size_t)(apduEnd - apduBegin), &result[0]+ index, resultLength);
result.resize(index + resultLength);
return resultLength;
}
bool PIVToken::getDataExists(const unsigned char *oid, size_t oidlen, const char *description)
{
byte_string result;
byte_string getDataApdu = buildGetData(byte_string(oid, oid + oidlen), 1);
uint16_t rx = simpleExchangeAPDU(getDataApdu, result);
if(rx == 0x6A82) return false;
if(rx == 0x6982) return true;
if(rx & 0xFF00 == SCARD_BYTES_LEFT_IN_SW2) return true;
if((rx >> 8) == PIV_RESULT_CONTINUATION_SW1) return true;
return result.size() > 0;
}