diff -up ./src/coolkey/coolkey.cpp.piv-ecc ./src/coolkey/coolkey.cpp --- ./src/coolkey/coolkey.cpp.piv-ecc 2013-09-08 15:50:33.085428102 -0700 +++ ./src/coolkey/coolkey.cpp 2013-09-08 15:50:33.119428673 -0700 @@ -34,7 +34,6 @@ #include "cky_base.h" #include "params.h" -#define NULL 0 /* static module data -------------------------------- */ @@ -70,11 +69,19 @@ typedef struct { /********************************************************************** ************************** MECHANISM TABLE *************************** **********************************************************************/ -static MechInfo -mechanismList[] = { + +static const MechInfo +rsaMechanismList[] = { {CKM_RSA_PKCS, { 1024, 4096, CKF_HW | CKF_SIGN | CKF_DECRYPT } } }; -static unsigned int numMechanisms = sizeof(mechanismList)/sizeof(MechInfo); + +static const MechInfo +ecMechanismList[] = { + {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDSA_SHA1, {256, 521, CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} } +}; + +unsigned int numRSAMechanisms = sizeof(rsaMechanismList)/sizeof(MechInfo); +unsigned int numECMechanisms = sizeof(ecMechanismList)/sizeof(MechInfo); /* ------------------------------------------------------------ */ @@ -166,7 +173,6 @@ NOTSUPPORTED(C_GenerateKey, (CK_SESSION_ NOTSUPPORTED(C_GenerateKeyPair, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_ATTRIBUTE_PTR,CK_ULONG,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR,CK_OBJECT_HANDLE_PTR)) NOTSUPPORTED(C_WrapKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_OBJECT_HANDLE,CK_BYTE_PTR,CK_ULONG_PTR)) NOTSUPPORTED(C_UnwrapKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_BYTE_PTR,CK_ULONG,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR)) -NOTSUPPORTED(C_DeriveKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR)) NOTSUPPORTED(C_GetFunctionStatus, (CK_SESSION_HANDLE)) NOTSUPPORTED(C_CancelFunction, (CK_SESSION_HANDLE)) @@ -200,6 +206,10 @@ SUPPORTED(C_SeedRandom, seedRandom, SUPPORTED(C_GenerateRandom, generateRandom, (CK_SESSION_HANDLE hSession ,CK_BYTE_PTR data,CK_ULONG dataLen), (hSession, data, dataLen)) +SUPPORTED(C_DeriveKey,derive, + (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey ), + (hSession, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey)) /* non-specialized functions supported with the slot directly */ @@ -249,7 +259,7 @@ C_Initialize(CK_VOID_PTR pInitArgs) log = new DummyLog(); } log->log("Initialize called, hello %d\n", 5); - CKY_SetName("coolkey"); + CKY_SetName((char *) "coolkey"); slotList = new SlotList(log); initialized = TRUE; return CKR_OK; @@ -347,6 +357,11 @@ CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { + + const MechInfo *mechanismList = NULL; + unsigned int numMechanisms = 0; + + if( ! initialized ) { return CKR_CRYPTOKI_NOT_INITIALIZED; } @@ -359,11 +374,21 @@ C_GetMechanismList(CK_SLOT_ID slotID, CK } slotList->validateSlotID(slotID); - if( ! slotList->getSlot( - slotIDToIndex(slotID))->isTokenPresent() ) { + + Slot *slot = slotList->getSlot(slotIDToIndex(slotID)); + + if( ! slot || ! slot->isTokenPresent() ) { return CKR_TOKEN_NOT_PRESENT; } + if ( slot->getIsECC()) { + mechanismList = ecMechanismList; + numMechanisms = numECMechanisms; + } else { + mechanismList = rsaMechanismList; + numMechanisms = numRSAMechanisms; + } + if( pMechanismList != NULL ) { if( *pulCount < numMechanisms ) { rv = CKR_BUFFER_TOO_SMALL; @@ -390,19 +415,36 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { + const MechInfo *mechanismList = NULL; + unsigned int numMechanisms = 0; + if( ! initialized ) { return CKR_CRYPTOKI_NOT_INITIALIZED; } + + try { log->log("C_GetMechanismInfo called\n"); if( pInfo == NULL ) { throw PKCS11Exception(CKR_ARGUMENTS_BAD); } slotList->validateSlotID(slotID); - if( ! slotList->getSlot(slotIDToIndex(slotID))->isTokenPresent() ) { + + + Slot *slot = slotList->getSlot(slotIDToIndex(slotID)); + + if( ! slot || ! slot->isTokenPresent() ) { return CKR_TOKEN_NOT_PRESENT; } + if ( slot->getIsECC()) { + mechanismList = ecMechanismList; + numMechanisms = numECMechanisms; + } else { + mechanismList = rsaMechanismList; + numMechanisms = numRSAMechanisms; + } + for(unsigned int i=0; i < numMechanisms; ++i ) { if( mechanismList[i].mech == type ) { *pInfo = mechanismList[i].info; diff -up ./src/coolkey/machdep.cpp.piv-ecc ./src/coolkey/machdep.cpp --- ./src/coolkey/machdep.cpp.piv-ecc 2013-09-08 15:50:33.085428102 -0700 +++ ./src/coolkey/machdep.cpp 2013-09-08 15:50:33.119428673 -0700 @@ -368,6 +368,7 @@ SHMem::initSegment(const char *name, int #ifdef FULL_CLEANUP flock(shmemData->fd, LOCK_UN); #endif + free(buf); delete shmemData; return NULL; } diff -up ./src/coolkey/object.cpp.piv-ecc ./src/coolkey/object.cpp --- ./src/coolkey/object.cpp.piv-ecc 2013-09-08 15:50:33.104428421 -0700 +++ ./src/coolkey/object.cpp 2013-09-08 15:50:33.121428706 -0700 @@ -25,12 +25,44 @@ using std::find_if; +const CKYByte rsaOID[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D, 0x01, 0x01,0x1}; +const CKYByte eccOID[] = {0x2a,0x86,0x48,0xce,0x3d,0x02,0x01}; + +#ifdef DEBUG +void dump(CKYBuffer *buf) +{ + CKYSize i; + CKYSize size = CKYBuffer_Size(buf); +#define ROW_LENGTH 60 + char string[ROW_LENGTH+1]; + char *bp = &string[0]; + CKYByte c; + + for (i=0; i < size; i++) { + if (i && ((i % (ROW_LENGTH-1)) == 0) ) { + *bp = 0; + printf(" %s\n",string); + bp = &string[0]; + } + c = CKYBuffer_GetChar(buf, i); + printf("%02x ",c); + *bp++ = (c < ' ') ? '.' : ((c & 0x80) ? '*' : c); + } + *bp = 0; + for (i= (i % (ROW_LENGTH-1)); i && (i < ROW_LENGTH); i++) { + printf(" "); + } + printf(" %s\n",string); + fflush(stdout); +} +#endif + bool AttributeMatch::operator()(const PKCS11Attribute& cmp) { return (attr->type == cmp.getType()) && - CKYBuffer_DataIsEqual(cmp.getValue(), - (const CKYByte *)attr->pValue, attr->ulValueLen); + CKYBuffer_DataIsEqual(cmp.getValue(), + (const CKYByte *)attr->pValue, attr->ulValueLen); } class AttributeTypeMatch @@ -45,14 +77,14 @@ class AttributeTypeMatch }; PKCS11Object::PKCS11Object(unsigned long muscleObjID_,CK_OBJECT_HANDLE handle_) - : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL) + : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL), keyType(unknown) { CKYBuffer_InitEmpty(&pubKey); } PKCS11Object::PKCS11Object(unsigned long muscleObjID_, const CKYBuffer *data, CK_OBJECT_HANDLE handle_) : muscleObjID(muscleObjID_), handle(handle_), - label(NULL), name(NULL) + label(NULL), name(NULL), keyType(unknown) { CKYBuffer_InitEmpty(&pubKey); @@ -63,9 +95,98 @@ PKCS11Object::PKCS11Object(unsigned long "PKCS #11 actual object id does not match stated id"); } if (type == 0) { - parseOldObject(data); + parseOldObject(data); } else if (type == 1) { - parseNewObject(data); + parseNewObject(data); + } +} + +SecretKey::SecretKey(unsigned long muscleObjID_, CK_OBJECT_HANDLE handle_, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) + : PKCS11Object(muscleObjID_, handle_) +{ + static CK_OBJECT_CLASS objClass = CKO_SECRET_KEY; + static CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + static CK_BBOOL value = 0x1; + + if ( secretKeyBuffer == NULL) + return; + + /* Rifle through the input template */ + + CK_ATTRIBUTE_TYPE type; + CK_ATTRIBUTE attr; + CK_ULONG valueLength = 0; + + for(int i = 0; i < (int) ulAttributeCount; i++) { + attr = pTemplate[i]; + type = attr.type; + + if ( type == CKA_VALUE_LEN) { + //CK_ULONG ulValueLen = attr.ulValueLen; + valueLength = *((CK_ULONG *)attr.pValue); + } else { + + CKYBuffer val; + CKYBuffer_InitFromData(&val,(const CK_BYTE *) attr.pValue, attr.ulValueLen); + setAttribute( type, &val); + CKYBuffer_FreeData(&val); + } + } + + adjustToKeyValueLength( secretKeyBuffer, valueLength ); + + /* Fall backs. */ + + if(!attributeExists(CKA_CLASS)) + setAttributeULong(CKA_CLASS, objClass); + + if(!attributeExists(CKA_KEY_TYPE)) + setAttributeULong(CKA_KEY_TYPE, keyType); + + if(!attributeExists(CKA_TOKEN)) + setAttributeBool(CKA_TOKEN, value); + + if(!attributeExists(CKA_DERIVE)) + setAttributeBool(CKA_DERIVE, value); + + /* Actual value */ + setAttribute(CKA_VALUE, secretKeyBuffer); + +} + +void SecretKey::adjustToKeyValueLength(CKYBuffer * secretKeyBuffer,CK_ULONG valueLength) +{ + const CK_LONG MAX_DIFF = 200; /* Put some bounds on this value */ + + if ( !secretKeyBuffer ) { + return; + } + + CKYBuffer scratch; + CK_ULONG actual_length = CKYBuffer_Size(secretKeyBuffer); + + CK_LONG diff = 0; + diff = (CK_LONG) valueLength - actual_length; + + if ( diff == 0 ) { + return; + } + + if ( diff > 0 && diff < MAX_DIFF ) { /*check for silly values */ + /* prepend with zeroes */ + CKYBuffer_InitFromLen(&scratch, diff); + CKYBuffer_AppendCopy(&scratch, secretKeyBuffer); + + CKYBuffer_FreeData(secretKeyBuffer); + CKYBuffer_InitFromCopy(secretKeyBuffer, &scratch); + CKYBuffer_FreeData(&scratch); + + } else if (diff < 0 ) { + /* truncate most significant bytes */ + CKYBuffer_InitFromData(&scratch, CKYBuffer_Data(secretKeyBuffer)-diff, valueLength); + CKYBuffer_FreeData(secretKeyBuffer); + CKYBuffer_InitFromCopy(secretKeyBuffer, &scratch); + CKYBuffer_FreeData(&scratch); } } @@ -95,29 +216,29 @@ PKCS11Object::parseOldObject(const CKYBu attrib.setType(CKYBuffer_GetLong(data, idx)); idx += 4; unsigned int attrLen = CKYBuffer_GetShort(data, idx); - idx += 2; + idx += 2; if( attrLen > CKYBuffer_Size(data) - || (idx + attrLen > CKYBuffer_Size(data)) ) { + || (idx + attrLen > CKYBuffer_Size(data)) ) { throw PKCS11Exception(CKR_DEVICE_ERROR, "Invalid attribute length %d\n", attrLen); } - /* these two types are ints, read them back from - * the card in host order */ - if ((attrib.getType() == CKA_CLASS) || - (attrib.getType() == CKA_CERTIFICATE_TYPE) || - (attrib.getType() == CKA_KEY_TYPE)) { - /* ulongs are 4 bytes on the token, even if they are 8 or - * more in the pkcs11 module */ - if (attrLen != 4) { + /* these two types are ints, read them back from + * the card in host order */ + if ((attrib.getType() == CKA_CLASS) || + (attrib.getType() == CKA_CERTIFICATE_TYPE) || + (attrib.getType() == CKA_KEY_TYPE)) { + /* ulongs are 4 bytes on the token, even if they are 8 or + * more in the pkcs11 module */ + if (attrLen != 4) { throw PKCS11Exception(CKR_DEVICE_ERROR, "Invalid attribute length %d\n", attrLen); - } - CK_ULONG value = makeLEUInt(data,idx); + } + CK_ULONG value = makeLEUInt(data,idx); - attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); - } else { - attrib.setValue(CKYBuffer_Data(data)+idx, attrLen); - } + attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); + } else { + attrib.setValue(CKYBuffer_Data(data)+idx, attrLen); + } idx += attrLen; attributes.push_back(attrib); } @@ -177,33 +298,33 @@ PKCS11Object::expandAttributes(unsigned unsigned long i; if (!attributeExists(CKA_ID)) { - PKCS11Attribute attrib; - attrib.setType(CKA_ID); - attrib.setValue(&cka_id, 1); + PKCS11Attribute attrib; + attrib.setType(CKA_ID); + attrib.setValue(&cka_id, 1); attributes.push_back(attrib); } /* unpack the class */ if (!attributeExists(CKA_CLASS)) { - PKCS11Attribute attrib; - attrib.setType(CKA_CLASS); - attrib.setValue((CKYByte *)&objectType, sizeof(CK_ULONG)); + PKCS11Attribute attrib; + attrib.setType(CKA_CLASS); + attrib.setValue((CKYByte *)&objectType, sizeof(CK_ULONG)); attributes.push_back(attrib); } /* unpack the boolean flags. Note, the default mask is based on * the class specified in fixedAttrs, not on the real class */ for (i=1; i < sizeof(unsigned long)*8; i++) { - unsigned long iMask = 1<< i; - if ((mask & iMask) == 0) { - continue; - } - if (attributeExists(boolType[i])) { - continue; - } - PKCS11Attribute attrib; - CKYByte bVal = (fixedAttrs & iMask) != 0; - attrib.setType(boolType[i]); - attrib.setValue(&bVal, 1); + unsigned long iMask = 1<< i; + if ((mask & iMask) == 0) { + continue; + } + if (attributeExists(boolType[i])) { + continue; + } + PKCS11Attribute attrib; + CKYByte bVal = (fixedAttrs & iMask) != 0; + attrib.setType(boolType[i]); + attrib.setValue(&bVal, 1); attributes.push_back(attrib); } } @@ -224,40 +345,40 @@ PKCS11Object::parseNewObject(const CKYBu // load up the explicit attributes first for (j=0, offset = 11; j < attributeCount && offset < size; j++) { PKCS11Attribute attrib; - CKYByte attributeDataType = CKYBuffer_GetChar(data, offset+4); - unsigned int attrLen = 0; + CKYByte attributeDataType = CKYBuffer_GetChar(data, offset+4); + unsigned int attrLen = 0; attrib.setType(CKYBuffer_GetLong(data, offset)); offset += 5; - switch(attributeDataType) { - case DATATYPE_STRING: - attrLen = CKYBuffer_GetShort(data, offset); - offset += 2; + switch(attributeDataType) { + case DATATYPE_STRING: + attrLen = CKYBuffer_GetShort(data, offset); + offset += 2; if (attrLen > CKYBuffer_Size(data) - || (offset + attrLen > CKYBuffer_Size(data)) ) { - throw PKCS11Exception(CKR_DEVICE_ERROR, - "Invalid attribute length %d\n", attrLen); + || (offset + attrLen > CKYBuffer_Size(data)) ) { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Invalid attribute length %d\n", attrLen); } - attrib.setValue(CKYBuffer_Data(data)+offset, attrLen); - break; - case DATATYPE_BOOL_FALSE: - case DATATYPE_BOOL_TRUE: - { - CKYByte bval = attributeDataType & 1; - attrib.setValue(&bval, 1); - } - break; - case DATATYPE_INTEGER: - { - CK_ULONG value = CKYBuffer_GetLong(data, offset); - attrLen = 4; - attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); - } - break; - default: - throw PKCS11Exception(CKR_DEVICE_ERROR, - "Invalid attribute Data Type %d\n", attributeDataType); - } + attrib.setValue(CKYBuffer_Data(data)+offset, attrLen); + break; + case DATATYPE_BOOL_FALSE: + case DATATYPE_BOOL_TRUE: + { + CKYByte bval = attributeDataType & 1; + attrib.setValue(&bval, 1); + } + break; + case DATATYPE_INTEGER: + { + CK_ULONG value = CKYBuffer_GetLong(data, offset); + attrLen = 4; + attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); + } + break; + default: + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Invalid attribute Data Type %d\n", attributeDataType); + } offset += attrLen; attributes.push_back(attrib); } @@ -276,7 +397,7 @@ static const CK_ATTRIBUTE rdr_templat bool PKCS11Object::matchesTemplate(const CK_ATTRIBUTE_PTR pTemplate, - CK_ULONG ulCount) + CK_ULONG ulCount) const { unsigned int i; @@ -285,10 +406,10 @@ PKCS11Object::matchesTemplate(const CK_A #if defined( NSS_HIDE_NONSTANDARD_OBJECTS ) if (!ulCount) { - // exclude MOZ reader objects from searches for all objects. - // To find an MOZ reader object, one must search for it by - // some matching attribute, such as class. - iterator iter = find_if(attributes.begin(), attributes.end(), + // exclude MOZ reader objects from searches for all objects. + // To find an MOZ reader object, one must search for it by + // some matching attribute, such as class. + iterator iter = find_if(attributes.begin(), attributes.end(), AttributeMatch(&rdr_template[0])); return (iter == attributes.end()) ? true : false; } @@ -325,7 +446,7 @@ PKCS11Object::getAttribute(CK_ATTRIBUTE_ AttributeTypeMatch(type)); if( iter == attributes.end() ) { - return NULL; + return NULL; } return iter->getValue(); } @@ -349,8 +470,9 @@ PKCS11Object::getAttributeValue(CK_ATTRI if( iter == attributes.end() ) { // no attribute of this type attrTypeInvalid = true; - log->log("GetAttributeValue: invalid type 0x%08x on object %x\n", - pTemplate[i].type, muscleObjID); + if ( log ) + log->log("GetAttributeValue: invalid type 0x%08x on object %x\n", + pTemplate[i].type, muscleObjID); pTemplate[i].ulValueLen = (CK_ULONG)-1; continue; } @@ -371,7 +493,7 @@ PKCS11Object::getAttributeValue(CK_ATTRI // the buffer is large enough. return the value and set the exact // length. memcpy(pTemplate[i].pValue, CKYBuffer_Data(iter->getValue()), - CKYBuffer_Size(iter->getValue())); + CKYBuffer_Size(iter->getValue())); pTemplate[i].ulValueLen = CKYBuffer_Size(iter->getValue()); } @@ -406,14 +528,14 @@ PKCS11Object::getLabel() // none found if( iter == attributes.end() ) { - return ""; + return ""; } int size = CKYBuffer_Size(iter->getValue()); label = new char [ size + 1 ]; if (!label) { - return ""; + return ""; } memcpy(label, CKYBuffer_Data(iter->getValue()), size); label[size] = 0; @@ -431,13 +553,13 @@ PKCS11Object::getClass() // none found */ if( iter == attributes.end() ) { - return (CK_OBJECT_CLASS) -1; + return (CK_OBJECT_CLASS) -1; } int size = CKYBuffer_Size(iter->getValue()); if (size != sizeof(objClass)) { - return (CK_OBJECT_CLASS) -1; + return (CK_OBJECT_CLASS) -1; } memcpy(&objClass, CKYBuffer_Data(iter->getValue()), size); @@ -453,7 +575,7 @@ PKCS11Object::setAttribute(CK_ATTRIBUTE_ iter = find_if(attributes.begin(), attributes.end(), AttributeTypeMatch(type)); if( iter != attributes.end() ) { - iter->setValue( CKYBuffer_Data(value), CKYBuffer_Size(value)); + iter->setValue( CKYBuffer_Data(value), CKYBuffer_Size(value)); } else { attributes.push_back(PKCS11Attribute(type, value)); } @@ -505,9 +627,15 @@ dataStart(const CKYByte *buf, unsigned i unsigned char tag; unsigned int used_length= 0; + *data_length = 0; /* make sure data_length is zero on failure */ + if(!buf) { return NULL; } + /* there must be at least 2 bytes */ + if (length < 2) { + return NULL; + } tag = buf[used_length++]; @@ -521,15 +649,22 @@ dataStart(const CKYByte *buf, unsigned i if (*data_length&0x80) { int len_count = *data_length & 0x7f; + if (len_count+used_length > length) { + return NULL; + } + *data_length = 0; while (len_count-- > 0) { *data_length = (*data_length << 8) | buf[used_length++]; } } + /* paranoia, can't happen */ + if (length < used_length) { + return NULL; + } if (*data_length > (length-used_length) ) { - *data_length = length-used_length; return NULL; } if (includeTag) *data_length += used_length; @@ -542,16 +677,158 @@ unwrapBitString(const CKYByte *buf, unsi { /* for RSA, bit string always has byte number of bits */ if (buf[0] != 0) { - return NULL; + return NULL; } if (len < 1) { - return NULL; + return NULL; } *retLen = len -1; return buf+1; } static SECStatus +GetECKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length, + CCItem *point, CCItem *params) +{ + const CKYByte *buf = spki_data; + unsigned int buf_length = spki_length; + const CKYByte *algid; + unsigned int algidlen; + const CKYByte *dummy; + unsigned int dummylen; + + if (!point || !params || !buf) + return SECFailure; + + point->data = NULL; + point->len = 0; + params->data = NULL; + params->len = 0; + + /* unwrap the algorithm id */ + dummy = dataStart(buf,buf_length,&dummylen,false); + if (dummy == NULL) return SECFailure; + buf_length -= (dummy-buf) + dummylen; + buf = dummy + dummylen; + /* unwrpped value is in dummy */ + algid = dummy; + algidlen = dummylen; + /* skip past algid oid */ + dummy = dataStart(algid, algidlen, &dummylen, false); + if (dummy == NULL) return SECFailure; + algidlen -= (dummy-algid) + dummylen; + algid = dummy + dummylen; + params->data = algid; + params->len = algidlen; + + /* unwrap the public key info */ + buf = dataStart(buf,buf_length,&buf_length,false); + if (buf == NULL) return SECFailure; + buf = unwrapBitString(buf,buf_length,&buf_length); + if (buf == NULL) return SECFailure; + + point->data = buf; + point->len = buf_length; + + if(point->data == NULL) return SECFailure; + + return SECSuccess; +} + +static bool +GetKeyOIDMatches(const CKYByte *spki_data, unsigned int length, const CKYByte *oid_data) +{ + bool ret = TRUE; + + if( spki_data == NULL || oid_data == NULL) { + return FALSE; + } + + for ( int i = 0 ; i < (int) length ; i++) { + if (spki_data[i] != oid_data[i]) { + ret = FALSE; + break; + } + + } + + return ret; +} + +static SECStatus +GetKeyAlgorithmId(const CKYByte *spki_data, unsigned int spki_length, + CCItem *algorithmId) +{ + + const CKYByte *buf = spki_data; + unsigned int buf_length = spki_length; + + if ( algorithmId == NULL) return SECFailure; + + /* objtain the algorithm id */ + algorithmId->data = dataStart(buf,buf_length,&algorithmId->len,false); + if (algorithmId->data == NULL) return SECFailure; + + return SECSuccess; + +} + +static PKCS11Object::KeyType +GetKeyTypeFromSPKI(const CKYBuffer *key) +{ + CCItem algIdItem; + SECStatus ret = GetKeyAlgorithmId(CKYBuffer_Data(key), + CKYBuffer_Size(key),&algIdItem); + PKCS11Object::KeyType foundType = PKCS11Object::unknown; + + if ( ret != SECSuccess ) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, + "Failed to decode key algorithm ID."); + } + + unsigned int length = 0; + const CKYByte *keyData = NULL; + + /* Get actual oid buffer */ + + keyData = dataStart(algIdItem.data,algIdItem.len,&length, false); + if (keyData == NULL) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, + "Failed to decode key algorithm ID."); + } + + bool match = FALSE; + + /* Check for outrageous length */ + + if ( length <= 3 || length >= algIdItem.len) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, + "Failed to decode key algorithm ID."); + } + /* check for RSA */ + + match = GetKeyOIDMatches(keyData, length, rsaOID); + + if ( match == TRUE ) { + foundType = PKCS11Object::rsa; + } else { + /* check for ECC */ + match = GetKeyOIDMatches(keyData, length, eccOID); + + if ( match == TRUE ) { + foundType = PKCS11Object::ecc; + } + + } + + if ( foundType == PKCS11Object::unknown) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, + "Failed to decode key algorithm ID."); + } + return foundType; +} + +static SECStatus GetKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length, CCItem *modulus, CCItem *exponent) { @@ -596,7 +873,7 @@ GetKeyFields(const CKYBuffer *spki, CKYB CCItem modulusItem, exponentItem; rv = GetKeyFieldItems(CKYBuffer_Data(spki), CKYBuffer_Size(spki), - &modulusItem, &exponentItem); + &modulusItem, &exponentItem); if( rv != SECSuccess ) { throw PKCS11Exception(CKR_FUNCTION_FAILED, @@ -607,6 +884,29 @@ GetKeyFields(const CKYBuffer *spki, CKYB CKYBuffer_Replace(exponent, 0, exponentItem.data, exponentItem.len); } +static void +GetECKeyFields(const CKYBuffer *spki, CKYBuffer *point, CKYBuffer *params) +{ + SECStatus rv; + CCItem pointItem, paramsItem; + + if (spki == NULL || point == NULL || params == NULL) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, + "Failed to decode certificate Subject Public KeyInfo!"); + } + + rv = GetECKeyFieldItems(CKYBuffer_Data(spki), CKYBuffer_Size(spki), + &pointItem, ¶msItem); + + if( rv != SECSuccess ) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, + "Failed to decode certificate Subject Public Key Info!"); + } + + CKYBuffer_Replace(point, 0, pointItem.data, pointItem.len); + CKYBuffer_Replace(params, 0, paramsItem.data, paramsItem.len); +} + Key::Key(unsigned long muscleObjID, const CKYBuffer *data, CK_OBJECT_HANDLE handle) : PKCS11Object(muscleObjID, data, handle) { @@ -616,22 +916,41 @@ Key::Key(unsigned long muscleObjID, cons CKYBuffer_InitEmpty(&empty); if ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY)) { - /* only CKK_RSA is supported */ - setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + //we may know already what type of key this is. + if (attributeExists(CKA_KEY_TYPE)) { + CK_ULONG type = 0; + CK_ATTRIBUTE aTemplate = {CKA_KEY_TYPE, &type, sizeof(CK_ULONG)}; + + getAttributeValue(&aTemplate, 1, NULL); + + if (type == 0x3) { + setKeyType(ecc); + setAttributeULong(CKA_KEY_TYPE, CKK_EC); + } else { + setKeyType(rsa); + setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + } + } else { + /* default to rsa */ + setKeyType(rsa); + setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + } + + // Could be RSA or ECC } else if (objClass == CKO_SECRET_KEY) { - if (!attributeExists(CKA_LABEL)) { - setAttribute(CKA_LABEL, &empty); - } - if (!attributeExists(CKA_KEY_TYPE)) { - /* default to DES3 */ - setAttributeULong(CKA_KEY_TYPE, CKK_DES3); - } + if (!attributeExists(CKA_LABEL)) { + setAttribute(CKA_LABEL, &empty); + } + if (!attributeExists(CKA_KEY_TYPE)) { + /* default to DES3 */ + setAttributeULong(CKA_KEY_TYPE, CKK_DES3); + } } if (!attributeExists(CKA_START_DATE)) { - setAttribute(CKA_START_DATE, &empty); + setAttribute(CKA_START_DATE, &empty); } if (!attributeExists(CKA_END_DATE)) { - setAttribute(CKA_END_DATE, &empty); + setAttribute(CKA_END_DATE, &empty); } } @@ -640,32 +959,59 @@ Key::completeKey(const PKCS11Object &cer { // infer key attributes from cert bool modulusExists, exponentExists; - CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus); - CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent); + bool pointExists, paramsExists; + + PKCS11Object::KeyType keyType; + const CKYBuffer *key = cert.getPubKey(); if (!attributeExists(CKA_LABEL)) { - setAttribute(CKA_LABEL, cert.getAttribute(CKA_LABEL)); + setAttribute(CKA_LABEL, cert.getAttribute(CKA_LABEL)); } + + CKYBuffer param1; CKYBuffer_InitEmpty(¶m1); + CKYBuffer param2; CKYBuffer_InitEmpty(¶m2); try { - modulusExists = attributeExists(CKA_MODULUS); - exponentExists = attributeExists(CKA_PUBLIC_EXPONENT); - if (!modulusExists || !exponentExists) { - const CKYBuffer *key = cert.getPubKey(); - GetKeyFields(key, &modulus, &exponent); - if (!modulusExists) { - setAttribute(CKA_MODULUS, &modulus); - } - if (!exponentExists) { - setAttribute(CKA_PUBLIC_EXPONENT, &exponent); - } - } + keyType = GetKeyTypeFromSPKI(key); + setKeyType(keyType); + + switch (keyType) { + case rsa: + modulusExists = attributeExists(CKA_MODULUS); + exponentExists = attributeExists(CKA_PUBLIC_EXPONENT); + if (!modulusExists || !exponentExists) { + GetKeyFields(key, ¶m1, ¶m2); + if (!modulusExists) { + setAttribute(CKA_MODULUS, ¶m1); + } + if (!exponentExists) { + setAttribute(CKA_PUBLIC_EXPONENT, ¶m2); + } + } + break; + case ecc: + pointExists = attributeExists(CKA_EC_POINT); + paramsExists = attributeExists(CKA_EC_PARAMS); + + if (!pointExists || !paramsExists) { + GetECKeyFields(key, ¶m1, ¶m2); + if (!pointExists) { + setAttribute(CKA_EC_POINT, ¶m1); + } + if (!paramsExists) { + setAttribute(CKA_EC_PARAMS, ¶m2); + } + } + break; + default: + break; + } } catch (PKCS11Exception &e) { - CKYBuffer_FreeData(&modulus); - CKYBuffer_FreeData(&exponent); - throw e; + CKYBuffer_FreeData(¶m1); + CKYBuffer_FreeData(¶m2); + throw e; } - CKYBuffer_FreeData(&modulus); - CKYBuffer_FreeData(&exponent); + CKYBuffer_FreeData(¶m1); + CKYBuffer_FreeData(¶m2); } static SECStatus @@ -737,14 +1083,14 @@ GetCertFieldItems(const CKYByte *dercert static void GetCertFields(const CKYBuffer *derCert, CKYBuffer *derSerial, - CKYBuffer *derSubject, CKYBuffer *derIssuer, CKYBuffer *subjectKey) + CKYBuffer *derSubject, CKYBuffer *derIssuer, CKYBuffer *subjectKey) { SECStatus rv; CCItem issuerItem, serialItem, derSerialItem, subjectItem, validityItem, subjectKeyItem; rv = GetCertFieldItems(CKYBuffer_Data(derCert), CKYBuffer_Size(derCert), - &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem, + &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem, &subjectKeyItem); if( rv != SECSuccess ) { @@ -769,50 +1115,50 @@ Cert::Cert(unsigned long muscleObjID, co CK_ULONG certTypeValue = CKC_X_509; CKYBuffer_InitFromData(&certType, (CKYByte *)&certTypeValue, - sizeof(certTypeValue)); + sizeof(certTypeValue)); CKYBuffer_Resize(&pubKey,0); try { - setAttribute(CKA_CERTIFICATE_TYPE, &certType); + setAttribute(CKA_CERTIFICATE_TYPE, &certType); - if (!attributeExists(CKA_VALUE)) { - if (derCert) { - setAttribute(CKA_VALUE, derCert); - } else { - throw PKCS11Exception(CKR_DEVICE_ERROR, - "Missing certificate data from token"); - } - } + if (!attributeExists(CKA_VALUE)) { + if (derCert) { + setAttribute(CKA_VALUE, derCert); + } else { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Missing certificate data from token"); + } + } - if (!derCert) { - derCert = getAttribute(CKA_VALUE); - if (!derCert) { - // paranoia, should never happen since we verify the - // attribute exists above - throw PKCS11Exception(CKR_DEVICE_ERROR, - "Missing certificate data from token"); - } - } + if (!derCert) { + derCert = getAttribute(CKA_VALUE); + if (!derCert) { + // paranoia, should never happen since we verify the + // attribute exists above + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Missing certificate data from token"); + } + } - // infer cert attributes + // infer cert attributes - GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); + GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); - if (!attributeExists(CKA_SERIAL_NUMBER)) { - setAttribute(CKA_SERIAL_NUMBER, &derSerial); - } - if (!attributeExists(CKA_SUBJECT)) { - setAttribute(CKA_SUBJECT, &derSubject); - } - if (!attributeExists(CKA_ISSUER)) { - setAttribute(CKA_ISSUER, &derIssuer); - } + if (!attributeExists(CKA_SERIAL_NUMBER)) { + setAttribute(CKA_SERIAL_NUMBER, &derSerial); + } + if (!attributeExists(CKA_SUBJECT)) { + setAttribute(CKA_SUBJECT, &derSubject); + } + if (!attributeExists(CKA_ISSUER)) { + setAttribute(CKA_ISSUER, &derIssuer); + } } catch (PKCS11Exception &e) { - CKYBuffer_FreeData(&certType); - CKYBuffer_FreeData(&derSerial); - CKYBuffer_FreeData(&derSubject); - CKYBuffer_FreeData(&derIssuer); - throw e; + CKYBuffer_FreeData(&certType); + CKYBuffer_FreeData(&derSerial); + CKYBuffer_FreeData(&derSubject); + CKYBuffer_FreeData(&derIssuer); + throw e; } CKYBuffer_FreeData(&certType); CKYBuffer_FreeData(&derSerial); @@ -822,7 +1168,7 @@ Cert::Cert(unsigned long muscleObjID, co Reader::Reader(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, const char *reader, const CKYBuffer *cardATR, bool isCoolkey) : - PKCS11Object(muscleObjID, handle) + PKCS11Object(muscleObjID, handle) { setAttributeULong(CKA_CLASS, CKO_MOZ_READER); setAttribute(CKA_LABEL, reader); @@ -833,9 +1179,10 @@ Reader::Reader(unsigned long muscleObjID setAttribute(CKA_MOZ_ATR, cardATR); } + CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert) : - PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16, - instance | 0x400) + PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16, + instance | 0x400) { CKYBuffer id; CKYBuffer empty; @@ -844,7 +1191,7 @@ CACPrivKey::CACPrivKey(CKYByte instance, /* So we know what the key is supposed to be used for based on * the instance */ if (instance == 2) { - decrypt = TRUE; + decrypt = TRUE; } CKYBuffer_InitEmpty(&empty); @@ -863,33 +1210,52 @@ CACPrivKey::CACPrivKey(CKYByte instance, setAttributeBool(CKA_LOCAL, TRUE); setAttributeULong(CKA_KEY_TYPE, CKK_RSA); - setAttributeBool(CKA_DECRYPT, decrypt); setAttributeBool(CKA_SIGN, !decrypt); setAttributeBool(CKA_SIGN_RECOVER, !decrypt); setAttributeBool(CKA_UNWRAP, FALSE); setAttributeBool(CKA_SENSITIVE, TRUE); setAttributeBool(CKA_EXTRACTABLE, FALSE); - CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus); - CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent); + CKYBuffer param1; CKYBuffer_InitEmpty(¶m1); + CKYBuffer param2; CKYBuffer_InitEmpty(¶m2); try { - const CKYBuffer *key = cert.getPubKey(); - GetKeyFields(key, &modulus, &exponent); - setAttribute(CKA_MODULUS, &modulus); - setAttribute(CKA_PUBLIC_EXPONENT, &exponent); - } catch (PKCS11Exception &e) { - CKYBuffer_FreeData(&modulus); - CKYBuffer_FreeData(&exponent); - throw e; - } - CKYBuffer_FreeData(&modulus); - CKYBuffer_FreeData(&exponent); + const CKYBuffer *key = cert.getPubKey(); + keyType = GetKeyTypeFromSPKI(key); + setKeyType(keyType); + + switch (keyType) { + case rsa: + GetKeyFields(key, ¶m1, ¶m2); + setAttribute(CKA_MODULUS, ¶m1); + setAttribute(CKA_PUBLIC_EXPONENT, ¶m2); + setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + setAttributeBool(CKA_DECRYPT, decrypt); + setAttributeBool(CKA_DERIVE, FALSE); + break; + case ecc: + GetECKeyFields(key, ¶m1, ¶m2); + setAttribute(CKA_EC_POINT, ¶m1); + setAttribute(CKA_EC_PARAMS, ¶m2); + setAttributeULong(CKA_KEY_TYPE, CKK_EC); + setAttributeBool(CKA_DECRYPT, FALSE); + setAttributeBool(CKA_DERIVE, decrypt); + break; + default: + break; + } + } catch (PKCS11Exception &e) { + CKYBuffer_FreeData(¶m1); + CKYBuffer_FreeData(¶m2); + throw e; + } + CKYBuffer_FreeData(¶m1); + CKYBuffer_FreeData(¶m2); } CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert) : - PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16, - instance | 0x500) + PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16, + instance | 0x500) { CKYBuffer id; CKYBuffer empty; @@ -898,7 +1264,7 @@ CACPubKey::CACPubKey(CKYByte instance, c /* So we know what the key is supposed to be used for based on * the instance */ if (instance == 2) { - encrypt = TRUE; + encrypt = TRUE; } CKYBuffer_InitEmpty(&empty); @@ -915,34 +1281,49 @@ CACPubKey::CACPubKey(CKYByte instance, c setAttribute(CKA_END_DATE, &empty); setAttributeBool(CKA_DERIVE, FALSE); setAttributeBool(CKA_LOCAL, TRUE); - setAttributeULong(CKA_KEY_TYPE, CKK_RSA); setAttributeBool(CKA_ENCRYPT, encrypt); setAttributeBool(CKA_VERIFY, !encrypt); setAttributeBool(CKA_VERIFY_RECOVER, !encrypt); setAttributeBool(CKA_WRAP, FALSE); - CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus); - CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent); + CKYBuffer param1; CKYBuffer_InitEmpty(¶m1); + CKYBuffer param2; CKYBuffer_InitEmpty(¶m2); try { - const CKYBuffer *key = cert.getPubKey(); - GetKeyFields(key, &modulus, &exponent); - setAttribute(CKA_MODULUS, &modulus); - setAttribute(CKA_PUBLIC_EXPONENT, &exponent); - } catch (PKCS11Exception &e) { - CKYBuffer_FreeData(&modulus); - CKYBuffer_FreeData(&exponent); - throw e; - } - CKYBuffer_FreeData(&modulus); - CKYBuffer_FreeData(&exponent); + const CKYBuffer *key = cert.getPubKey(); + keyType = GetKeyTypeFromSPKI(key); + setKeyType(keyType); + + switch (keyType) { + case rsa: + GetKeyFields(key, ¶m1, ¶m2); + setAttribute(CKA_MODULUS, ¶m1); + setAttribute(CKA_PUBLIC_EXPONENT, ¶m2); + setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + break; + case ecc: + GetECKeyFields(key, ¶m1, ¶m2); + setAttribute(CKA_EC_POINT, ¶m1); + setAttribute(CKA_EC_PARAMS, ¶m2); + setAttributeULong(CKA_KEY_TYPE, CKK_EC); + break; + default: + break; + } + } catch (PKCS11Exception &e) { + CKYBuffer_FreeData(¶m1); + CKYBuffer_FreeData(¶m2); + throw e; + } + CKYBuffer_FreeData(¶m1); + CKYBuffer_FreeData(¶m2); } static const char *CAC_Label[] = { - "CAC ID Certificate", - "CAC Email Signature Certificate", - "CAC Email Encryption Certificate", + "CAC ID Certificate", + "CAC Email Signature Certificate", + "CAC Email Encryption Certificate", }; static const unsigned char CN_DATA[] = { 0x55, 0x4, 0x3 }; @@ -959,39 +1340,43 @@ GetCN(const CKYByte *dn, unsigned int dn if (buf == NULL) return SECFailure; while (buf_length) { - const CKYByte *name; - unsigned int name_length; - const CKYByte *oid; - unsigned int oid_length; - - /* unwrap the set */ - name = dataStart(buf, buf_length, &name_length, false); + const CKYByte *name; + unsigned int name_length; + const CKYByte *oid; + unsigned int oid_length; + + /* unwrap the set */ + name = dataStart(buf, buf_length, &name_length, false); + if (name == NULL) return SECFailure; /* advance to next set */ - buf_length -= (name-buf) + name_length; - buf = name + name_length; + buf_length -= (name-buf) + name_length; + buf = name + name_length; - /* unwrap the Sequence */ - name = dataStart(name, name_length, &name_length, false); + /* unwrap the Sequence */ + name = dataStart(name, name_length, &name_length, false); + if (name == NULL) return SECFailure; /* unwrap the oid */ - oid = dataStart(name, name_length, &oid_length, false); + oid = dataStart(name, name_length, &oid_length, false); + if (oid == NULL) return SECFailure; - /* test the oid */ - if (oid_length != CN_LENGTH) { - continue; - } - if (memcmp(oid, CN_DATA, CN_LENGTH) != 0) { - continue; - } + /* test the oid */ + if (oid_length != CN_LENGTH) { + continue; + } + if (memcmp(oid, CN_DATA, CN_LENGTH) != 0) { + continue; + } - /* advance to CN */ - name_length -= (oid-name) + oid_length; - name = oid + oid_length; - - /* unwrap the CN */ - cn->data = dataStart(name, name_length, &cn->len, false); - return SECSuccess; + /* advance to CN */ + name_length -= (oid-name) + oid_length; + name = oid + oid_length; + + /* unwrap the CN */ + cn->data = dataStart(name, name_length, &cn->len, false); + if (cn->data == NULL) return SECFailure; + return SECSuccess; } return SECFailure; } @@ -1006,11 +1391,11 @@ GetUserName(const CKYBuffer *dn) rv = GetCN(CKYBuffer_Data(dn), CKYBuffer_Size(dn) , &cn); if( rv != SECSuccess ) { - return NULL; + return NULL; } string = new char [ cn.len + 1 ]; if (string == NULL) { - return NULL; + return NULL; } memcpy(string, cn.data, cn.len); string[cn.len] = 0; @@ -1018,8 +1403,8 @@ GetUserName(const CKYBuffer *dn) } CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert) : - PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, - instance | 0x600) + PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, + instance | 0x600) { CKYBuffer id; CKYBuffer empty; @@ -1028,7 +1413,7 @@ CACCert::CACCert(CKYByte instance, const /* So we know what the key is supposed to be used for based on * the instance */ if (instance == 2) { - decrypt = TRUE; + decrypt = TRUE; } CKYBuffer_InitEmpty(&empty); @@ -1050,19 +1435,19 @@ CACCert::CACCert(CKYByte instance, const CKYBuffer_Resize(&pubKey,0); try { - setAttribute(CKA_VALUE, derCert); - // infer cert attributes + setAttribute(CKA_VALUE, derCert); + // infer cert attributes - GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); + GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); - setAttribute(CKA_SERIAL_NUMBER, &derSerial); - setAttribute(CKA_SUBJECT, &derSubject); - setAttribute(CKA_ISSUER, &derIssuer); + setAttribute(CKA_SERIAL_NUMBER, &derSerial); + setAttribute(CKA_SUBJECT, &derSubject); + setAttribute(CKA_ISSUER, &derIssuer); } catch (PKCS11Exception &e) { - CKYBuffer_FreeData(&derSerial); - CKYBuffer_FreeData(&derSubject); - CKYBuffer_FreeData(&derIssuer); - throw e; + CKYBuffer_FreeData(&derSerial); + CKYBuffer_FreeData(&derSubject); + CKYBuffer_FreeData(&derIssuer); + throw e; } name = GetUserName(&derSubject); /* adopt */ @@ -1070,3 +1455,100 @@ CACCert::CACCert(CKYByte instance, const CKYBuffer_FreeData(&derSubject); CKYBuffer_FreeData(&derIssuer); } + +DEREncodedSignature::DEREncodedSignature(const CKYBuffer *derSig) +{ + + CKYBuffer_InitEmpty(&derEncodedSignature); + CKYBuffer_InitFromCopy(&derEncodedSignature, derSig); +} + +DEREncodedSignature::~DEREncodedSignature() +{ + CKYBuffer_FreeData(&derEncodedSignature); +} + +int DEREncodedSignature::getRawSignature(CKYBuffer *rawSig, + unsigned int keySize) +{ + const CKYByte *buf = NULL; + + if (rawSig == NULL) { + return -1; + } + + if (CKYBuffer_Size(&derEncodedSignature) == 0) { + return -1; + } + + CKYBuffer_Zero(rawSig); + + unsigned int seq_length = 0; + unsigned int expected_sig_len = ( (keySize + 7) / 8 ) * 2 ; + unsigned int expected_piece_size = expected_sig_len / 2 ; + + /* unwrap the sequence */ + buf = dataStart(CKYBuffer_Data(&derEncodedSignature), CKYBuffer_Size(&derEncodedSignature),&seq_length, false); + + if (buf == NULL) return -1; + + // unwrap first multi byte integer + + unsigned int int_length = 0; + const CKYByte *int1Buf = NULL; + const CKYByte *int2Buf = NULL; + + int1Buf = dataStart(buf, seq_length, &int_length, false ); + + if (int1Buf == NULL) return -1; + //advance to next entry + + if (int_length > expected_piece_size) { + + unsigned int diff = int_length - expected_piece_size ; + + /* Make sure we are chopping off zeroes + Otherwise give up. */ + + for (int i = 0 ; i < (int) diff ; i++) { + if ( int1Buf[i] != 0) + return -1; + } + + int_length -= diff; + int1Buf += diff; + + } + + seq_length -= (int1Buf -buf) + int_length; + buf = int1Buf + int_length; + + // unwrap second multi byte integer + + unsigned int second_int_length = 0; + + int2Buf = dataStart(buf, seq_length, &second_int_length, false); + + if (int2Buf == NULL) return -1; + + + if (second_int_length > expected_piece_size) { + unsigned int diff = second_int_length - expected_piece_size ; + + /* Make sure we are chopping off zeroes + Otherwise give up. */ + + for (int i = 0 ; i < (int) diff ; i++) { + if ( int2Buf[i] != 0) + return -1; + } + + second_int_length -= diff; + int2Buf += diff; + } + + CKYBuffer_AppendData(rawSig, int1Buf, int_length); + CKYBuffer_AppendData(rawSig, int2Buf, second_int_length); + + return CKYSUCCESS; +} diff -up ./src/coolkey/object.h.piv-ecc ./src/coolkey/object.h --- ./src/coolkey/object.h.piv-ecc 2013-09-08 15:50:33.081428035 -0700 +++ ./src/coolkey/object.h 2013-09-08 15:50:33.121428706 -0700 @@ -49,7 +49,7 @@ class PKCS11Attribute { CKYBuffer_Size(&cpy.value)); return *this; } - PKCS11Attribute() { CKYBuffer_InitEmpty(&value); } + PKCS11Attribute() : type(0){ CKYBuffer_InitEmpty(&value); } PKCS11Attribute(CK_ATTRIBUTE_TYPE type_, const CKYBuffer *value_) : type(type_) { CKYBuffer_InitFromCopy(&value, value_); } ~PKCS11Attribute() { CKYBuffer_FreeData(&value); } @@ -57,6 +57,11 @@ class PKCS11Attribute { class PKCS11Object { public: + enum KeyType { + rsa, + ecc, + unknown + }; typedef list AttributeList; typedef AttributeList::iterator AttributeIter; @@ -75,18 +80,20 @@ class PKCS11Object { PKCS11Object &operator=(PKCS11Object &cpy) { return *this; } //Disallow protected : - CKYBuffer pubKey; char *name; + KeyType keyType; + CKYBuffer pubKey; public: PKCS11Object(unsigned long muscleObjID, CK_OBJECT_HANDLE handle); PKCS11Object(unsigned long muscleObjID, const CKYBuffer *data, CK_OBJECT_HANDLE handle); - ~PKCS11Object() { delete [] label; delete [] name; CKYBuffer_FreeData(&pubKey); } + ~PKCS11Object() { delete label; delete name; CKYBuffer_FreeData(&pubKey); + attributes.clear(); } PKCS11Object(const PKCS11Object& cpy) : attributes(cpy.attributes), muscleObjID(cpy.muscleObjID), - handle(cpy.handle), label(NULL), name(NULL) { + handle(cpy.handle), label(NULL), name(NULL), keyType(cpy.keyType) { CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey); } @@ -116,14 +123,15 @@ class PKCS11Object { const CKYBuffer *getPubKey(void) const { return &pubKey; } + + KeyType getKeyType() const { return keyType;} + void setKeyType(KeyType theType) { keyType = theType; } }; class Key : public PKCS11Object { - public: Key(unsigned long muscleObjID, const CKYBuffer *data, CK_OBJECT_HANDLE handle); void completeKey(const PKCS11Object &cert); - }; class Cert : public PKCS11Object { @@ -153,6 +161,25 @@ class Reader : public PKCS11Object { const char *reader, const CKYBuffer *cardATR, bool isCoolkey); }; +class SecretKey : public PKCS11Object { + public: + SecretKey(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount); + private: + void adjustToKeyValueLength(CKYBuffer * secretKeyBuffer,CK_ULONG valueLength); + +}; + +class DEREncodedSignature { + + protected : + CKYBuffer derEncodedSignature; + public: + DEREncodedSignature(const CKYBuffer *derSig); + ~DEREncodedSignature(); + int getRawSignature(CKYBuffer *rawSig, unsigned int keySize); + +}; + class AttributeMatch { private: diff -up ./src/coolkey/pkcs11t.h.piv-ecc ./src/coolkey/pkcs11t.h --- ./src/coolkey/pkcs11t.h.piv-ecc 2006-06-09 11:39:11.000000000 -0700 +++ ./src/coolkey/pkcs11t.h 2013-09-08 15:50:33.122428723 -0700 @@ -1351,4 +1351,41 @@ typedef struct CK_PKCS5_PBKD2_PARAMS { typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR; +/* The following EC Key Derivation Functions are defined */ + +#define CKD_NULL 0x00000001 + +#define CKD_SHA1_KDF 0x00000002 + +/* CK_ECDH1_DERIVE_PARAMS is new for v2.11. */ + + typedef CK_ULONG CK_EC_KDF_TYPE; + +/* + * CK_ECDH1_DERIVE_PARAMS provides the parameters to the + * + * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms, + * + * where each party contributes one key pair. + * + */ + +typedef struct CK_ECDH1_DERIVE_PARAMS { + + CK_EC_KDF_TYPE kdf; + + CK_ULONG ulSharedDataLen; + + CK_BYTE_PTR pSharedData; + + CK_ULONG ulPublicDataLen; + + CK_BYTE_PTR pPublicData; + +} CK_ECDH1_DERIVE_PARAMS; + + + +typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR; + #endif diff -up ./src/coolkey/slot.cpp.piv-ecc ./src/coolkey/slot.cpp --- ./src/coolkey/slot.cpp.piv-ecc 2013-09-08 15:50:33.112428555 -0700 +++ ./src/coolkey/slot.cpp 2013-09-08 15:50:33.124428757 -0700 @@ -54,6 +54,34 @@ const CKYByte ATR2[] = { 0x3B, 0x6F, 0x00, 0xFF, 0x52, 0x53, 0x41, 0x53, 0x65, 0x63, 0x75, 0x72, 0x49, 0x44, 0x28, 0x52, 0x29, 0x31, 0x30 }; + +/* ECC curve information + * Provide information for the limited set of curves supported by our smart card(s). + * + */ + +typedef struct curveBytes2Name { + const CKYByte * bytes; + const char *curveName; + unsigned int length; + +} CurveBytes2Name; + +/* First byte is length of oid byte array. */ + +const CKYByte nistp256[] = { 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; +const CKYByte nistp384[] = { 0x5, 0x2b, 0x81, 0x04, 0x00, 0x22 }; +const CKYByte nistp521[] = { 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 }; + +const int numECCurves = 3; + +static CurveBytes2Name curveBytesNamePair[] = +{ + { nistp256, "nistp256", 256 }, + { nistp384, "nistp384", 384 }, + { nistp521, "nistp521", 521 } +}; + SlotList::SlotList(Log *log_) : log(log_) { // initialize things to NULL so we can recover from an exception @@ -136,7 +164,11 @@ SlotList::updateSlotList() throw PKCS11Exception(CKR_HOST_MEMORY); memset(newSlots, 0, numReaders*sizeof(Slot*)); - memcpy(newSlots, slots, sizeof(slots[0]) * numSlots); + /* keep coverity happy, even though slot == NULL implies that + * numSlots == 0 */ + if (slots) { + memcpy(newSlots, slots, sizeof(slots[0]) * numSlots); + } for (unsigned int i=numSlots; i < numReaders; i++) { newSlots[i] = new @@ -237,32 +269,19 @@ SlotList::updateReaderList() CKYStatus status = CKYCardContext_ListReaders(context, &readerNames); if ( status != CKYSUCCESS ) { - throw PKCS11Exception(CKR_GENERAL_ERROR, + /* if the service is stopped, treat it as if we have no readers */ + if ((CKYCardContext_GetLastError(context) != SCARD_E_NO_SERVICE) && + (CKYCardContext_GetLastError(context) != SCARD_E_SERVICE_STOPPED)) { + throw PKCS11Exception(CKR_GENERAL_ERROR, "Failed to list readers: 0x%x\n", CKYCardContext_GetLastError(context)); + } } - if (!readerStates) { + if (readerStates == NULL && readerNames != NULL) { /* fresh Reader State list, just create it */ readerStates = CKYReader_CreateArray(readerNames, (CKYSize *)&numReaders); - /* if we have no readers, make sure we have at least one to keep things - * happy */ - if (readerStates == NULL && - CKYReaderNameList_GetCount(readerNames) == 0) { - readerStates = (SCARD_READERSTATE *) - malloc(sizeof(SCARD_READERSTATE)); - if (readerStates) { - CKYReader_Init(readerStates); - status = CKYReader_SetReaderName(readerStates, "E-Gate 0 0"); - if (status != CKYSUCCESS) { - CKYReader_DestroyArray(readerStates, 1); - readerStates = NULL; - } else { - numReaders = 1; - } - } - } CKYReaderNameList_Destroy(readerNames); if (readerStates == NULL) { @@ -272,6 +291,16 @@ SlotList::updateReaderList() return; } + if (readerStates == NULL) { + /* if we didn't have any readers before and we did get new names, + * that is handled above. If we didn't have any readers before, and + * we didn't get any names, there is nothing to update. blow out now. + * This more efficient and makes coverity happy (since coverity doesn't + * know numReaders and readerStates are linked). */ + return; + } + + /* it would be tempting at this point just to see if we have more readers * then specified previously. The problem with this is it is possible that * some readers have been deleted, so the only way to tell if we have @@ -286,18 +315,26 @@ SlotList::updateReaderList() const char *curReaderName = NULL; unsigned long knownState = 0; - for(int ri = 0 ; ri < numReaders; ri ++) { + for(unsigned int ri = 0 ; ri < numReaders; ri ++) { knownState = CKYReader_GetKnownState(&readerStates[ri]); - + curReaderName = CKYReader_GetReaderName(&readerStates[ri]); - if(readerNameExistsInList(curReaderName,&readerNames)) { - CKYReader_SetKnownState(&readerStates[ri], knownState & ~SCARD_STATE_IGNORE); + if(readerNames && readerNameExistsInList(curReaderName,&readerNames)) { + CKYReader_SetKnownState(&readerStates[ri], + knownState & ~SCARD_STATE_IGNORE); } else { - if (!(knownState & SCARD_STATE_UNAVAILABLE)) - CKYReader_SetKnownState(&readerStates[ri], knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED); - } + if (!(knownState & SCARD_STATE_UNAVAILABLE)) + CKYReader_SetKnownState(&readerStates[ri], + knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED); + } } + if (readerNames == NULL) { + /* OK we've marked everything unavailable, we clearly + * aren't adding any readers, so we can blow out here */ + return; + } + const char *newReadersData[MAX_READER_DELTA]; const char **newReaders = &newReadersData[0]; unsigned int newReaderCount = 0; @@ -370,7 +407,8 @@ Slot::Slot(const char *readerName_, Log : log(log_), readerName(NULL), personName(NULL), manufacturer(NULL), slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), isVersion1Key(false), needLogin(false), fullTokenName(false), - mCoolkey(false), mOldCAC(false), + mCoolkey(false), mOldCAC(false),mCACLocalLogin(false), + pivContainer(-1), pivKey(-1), mECC(false), #ifdef USE_SHMEM shmem(readerName_), #endif @@ -573,6 +611,35 @@ SlotList::getSlotList(CK_BBOOL tokenPres return rv; } +bool +Slot::getPIVLoginType(void) +{ + CKYStatus status; + CKYISOStatus apduRC; + CKYBuffer buffer; + bool local = true; + + CKYBuffer_InitEmpty(&buffer); + + /* get the discovery object */ + status = PIVApplet_GetCertificate(conn, &buffer, 0x7e, &apduRC); + if (status != CKYSUCCESS) { + /* Discovery object optional, PIV defaults to local */ + goto done; + } + /* techically we probably should parse out the TLVs, but the PIV + * specifies exactly what they should be, so we know exactly which + * byte to look at */ + if ((CKYBuffer_Size(&buffer) >= 20) && + (CKYBuffer_GetChar(&buffer,17) == 0x60)) { + /* This tells us we should use global login for this piv card */ + local = false; + } +done: + CKYBuffer_FreeData(&buffer); + return true; +} + void Slot::connectToToken() { @@ -587,6 +654,7 @@ Slot::connectToToken() if( ! CKYCardConnection_IsConnected(conn) ) { int i = 0; //for cranky readers try again a few more times + status = CKYSCARDERR; while( i++ < 5 && status != CKYSUCCESS ) { status = CKYCardConnection_Connect(conn, readerName); @@ -672,12 +740,36 @@ Slot::connectToToken() // see if the applet is selectable log->log("time connnect: Begin transaction %d ms\n", OSTimeNow() - time); + status = PIVApplet_Select(conn, NULL); + if (status == CKYSUCCESS) { + /* CARD is a PIV card */ + state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; + isVersion1Key = 0; + needLogin = 1; + mCoolkey = 0; + mOldCAC = 0; + mCACLocalLogin = getPIVLoginType(); + return; + } status = CKYApplet_SelectCoolKeyManager(conn, NULL); if (status != CKYSUCCESS) { log->log("CoolKey Select failed 0x%x\n", status); status = getCACAid(); if (status != CKYSUCCESS) { - goto loser; + log->log("CAC Select failed 0x%x\n", status); + if (status == CKYSCARDERR) { + log->log("Card Failure 0x%x\n", + CKYCardConnection_GetLastError(conn)); + disconnect(); + } + /* CARD is a PIV card */ + state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; + isVersion1Key = 0; + needLogin = 1; + mCoolkey = 0; + mOldCAC = 0; + mCACLocalLogin = getPIVLoginType(); + return; } state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; /* skip the read of the cuid. We really don't need it and, @@ -687,15 +779,7 @@ Slot::connectToToken() isVersion1Key = 0; needLogin = 1; mCoolkey = 0; - return; - -loser: - log->log("CAC Select failed 0x%x\n", status); - if (status == CKYSCARDERR) { - log->log("CAC Card Failure 0x%x\n", - CKYCardConnection_GetLastError(conn)); - disconnect(); - } + mCACLocalLogin = false; return; } mCoolkey = 1; @@ -762,8 +846,8 @@ Slot::invalidateLogin(bool hard) } } else { loggedIn = false; + pinCache.invalidate(); if (hard) { - pinCache.invalidate(); pinCache.clearPin(); } } @@ -1202,6 +1286,7 @@ Slot::getTokenInfo(CK_TOKEN_INFO_PTR pTo return CKR_OK; + } void @@ -1222,7 +1307,16 @@ SlotList::waitForSlotEvent(CK_FLAGS flag bool found = FALSE; CKYStatus status; SCARD_READERSTATE *myReaderStates = NULL; + static SCARD_READERSTATE pnp = { 0 }; unsigned int myNumReaders = 0; + + readerListLock.getLock(); + if (pnp.szReader == 0) { + CKYReader_Init(&pnp); + pnp.szReader = "\\\\?PnP?\\Notification"; + } + readerListLock.releaseLock(); + #ifndef notdef do { readerListLock.getLock(); @@ -1241,11 +1335,13 @@ SlotList::waitForSlotEvent(CK_FLAGS flag * from that set to return */ for (i=0; i < numReaders; i++) { - unsigned long knownState = CKYReader_GetKnownState(&readerStates[i]); + unsigned long knownState = + CKYReader_GetKnownState(&readerStates[i]); if ((knownState & SCARD_STATE_UNAVAILABLE) && (knownState & SCARD_STATE_CHANGED)) { - CKYReader_SetKnownState(&readerStates[i], knownState & ~SCARD_STATE_CHANGED); + CKYReader_SetKnownState(&readerStates[i], + knownState & ~SCARD_STATE_CHANGED); readerListLock.releaseLock(); *slotp = slotIndexToID(i); found = TRUE; @@ -1262,31 +1358,48 @@ SlotList::waitForSlotEvent(CK_FLAGS flag break; } - if (myNumReaders != numReaders) { + if (myNumReaders != numReaders + 1) { if (myReaderStates) { delete [] myReaderStates; } - myReaderStates = new SCARD_READERSTATE [numReaders]; + myReaderStates = new SCARD_READERSTATE [numReaders + 1]; + myNumReaders = numReaders + 1; } - memcpy(myReaderStates, readerStates, - sizeof(SCARD_READERSTATE)*numReaders); - myNumReaders = numReaders; + + memcpy(myReaderStates, readerStates, + sizeof(SCARD_READERSTATE) * numReaders); + memcpy(&myReaderStates[numReaders], &pnp, sizeof(pnp)); readerListLock.releaseLock(); status = CKYCardContext_WaitForStatusChange(context, - myReaderStates, myNumReaders, timeout); + myReaderStates, myNumReaders, timeout); if (status == CKYSUCCESS) { - for (i=0; i < myNumReaders; i++) { - SCARD_READERSTATE *rsp = &myReaderStates[i]; - unsigned long eventState = CKYReader_GetEventState(rsp); + unsigned long eventState; + for (i=0; i < myNumReaders - 1; i++) { + eventState = CKYReader_GetEventState(&myReaderStates[i]); if (eventState & SCARD_STATE_CHANGED) { readerListLock.getLock(); - CKYReader_SetKnownState(&readerStates[i], eventState & ~SCARD_STATE_CHANGED); + CKYReader_SetKnownState(&readerStates[i], + eventState & ~SCARD_STATE_CHANGED); readerListLock.releaseLock(); *slotp = slotIndexToID(i); found = TRUE; break; } } + /* No real need to check for an additional card, we already update + * the list when we iterate. */ + if (!found) { + eventState = CKYReader_GetEventState( + &myReaderStates[myNumReaders-1]); + if (eventState & SCARD_STATE_CHANGED) { + readerListLock.getLock(); + CKYReader_SetKnownState(&pnp, + eventState & ~SCARD_STATE_CHANGED); + readerListLock.releaseLock(); + log->log("Reader insertion/removal detected\n"); + continue; /* get the update */ + } + } } if (found || (flag == CKF_DONT_BLOCK) || shuttingDown) { @@ -1294,21 +1407,20 @@ SlotList::waitForSlotEvent(CK_FLAGS flag } #ifndef WIN32 - if (status != CKYSUCCESS) { - - if ( (CKYCardContext_GetLastError(context) == - SCARD_E_READER_UNAVAILABLE) || - (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) - { - OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY); - } - - - } + /* pcsc-lite needs to make progress or something */ + if (status != CKYSUCCESS) { + if ((CKYCardContext_GetLastError(context) == + SCARD_E_READER_UNAVAILABLE) || + (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) { + OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY); + } + } #endif } while ((status == CKYSUCCESS) || (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT) || - ( CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE)); + (CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE) || + (CKYCardContext_GetLastError(context) == SCARD_E_NO_SERVICE) || + (CKYCardContext_GetLastError(context) == SCARD_E_SERVICE_STOPPED) ); #else do { OSSleep(100); @@ -1345,6 +1457,7 @@ Slot::handleConnectionError() case SCARD_W_REMOVED_CARD: ckrv = CKR_DEVICE_REMOVED; break; + default: ckrv = CKR_DEVICE_ERROR; break; @@ -1404,13 +1517,46 @@ Slot::selectApplet() } void -Slot::selectCACApplet(CKYByte instance) +Slot::selectCACApplet(CKYByte instance, bool doDisconnect) { CKYStatus status; + /* PIV containers and keys by instance */ + static const int container[] = { + 0x5fc105, 0x5fc10a, 0x5fc10b, 0x5fc101, + 0x5fc10d, 0x5fc10e, 0x5fc10f, 0x5fc110, + 0x5fc111, 0x5fc112, 0x5fc113, 0x5fc114, + 0x5fc115, 0x5fc116, 0x5fc117, 0x5fc118, + 0x5fc119, 0x5fc11a, 0x5fc11b, 0x5fc11c, + 0x5fc11d, 0x5fc11e, 0x5fc11f, 0x5fc120 + }; + static const int keyRef[] = { + 0x9a, 0x9c, 0x9d, 0x9e, + 0x82, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, + 0x8e, 0x8f, 0x90, 0x91, + 0x92, 0x93, 0x94, 0x95 + }; + + if (state & PIV_CARD) { + status = PIVApplet_Select(conn, NULL); + if (status == CKYSCARDERR) handleConnectionError(); + if (status != CKYSUCCESS) { + if (doDisconnect) { + disconnect(); + } + throw PKCS11Exception(CKR_DEVICE_REMOVED); + } + pivContainer = container[instance]; + pivKey = keyRef[instance]; + return; + } CKYBuffer *aid = &cardAID[instance]; if (CKYBuffer_Size(aid) == 0) { - disconnect(); + if (doDisconnect) { + disconnect(); + } throw PKCS11Exception(CKR_DEVICE_REMOVED); return; } @@ -1419,7 +1565,9 @@ Slot::selectCACApplet(CKYByte instance) if ( status == CKYSCARDERR ) handleConnectionError(); if ( status != CKYSUCCESS) { // could not select applet: this just means it's not there - disconnect(); + if (doDisconnect) { + disconnect(); + } throw PKCS11Exception(CKR_DEVICE_REMOVED); } if (mOldCAC) { @@ -1428,7 +1576,9 @@ Slot::selectCACApplet(CKYByte instance) status = CACApplet_SelectFile(conn, cardEF[instance], NULL); if ( status == CKYSCARDERR ) handleConnectionError(); if ( status != CKYSUCCESS) { - disconnect(); + if (doDisconnect) { + disconnect(); + } throw PKCS11Exception(CKR_DEVICE_REMOVED); } } @@ -1521,6 +1671,29 @@ Slot::generateUnusedObjectHandle() return handle; } +/* Create a short lived Secret Key for ECC key derive. */ +PKCS11Object * +Slot::createSecretKeyObject(CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) +{ + + if (secretKeyBuffer == NULL ) { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Can't create secret key object for ECC."); + } + + unsigned long muscleID = 0xfff; + PKCS11Object *secret = new SecretKey(muscleID, handle, secretKeyBuffer, pTemplate, ulAttributeCount); + + if (secret == NULL) { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Can't create secret key object for ECC."); + } + + tokenObjects.push_back(*secret); + + return secret; +} + void Slot::addKeyObject(list& objectList, const ListObjectInfo& info, CK_OBJECT_HANDLE handle, bool isCombined) @@ -1530,24 +1703,32 @@ Slot::addKeyObject(list& o CK_OBJECT_CLASS objClass = keyObj.getClass(); const CKYBuffer *id; - if (isCombined && - ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY))) { - id = keyObj.getAttribute(CKA_ID); - if ((!id) || (CKYBuffer_Size(id) != 1)) { - throw PKCS11Exception(CKR_DEVICE_ERROR, - "Missing or invalid CKA_ID value"); - } - iter = find_if(objectList.begin(), objectList.end(), - ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0))); - if ( iter == objectList.end() ) { + ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY))) { + id = keyObj.getAttribute(CKA_ID); + if ((!id) || (CKYBuffer_Size(id) != 1)) { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Missing or invalid CKA_ID value"); + } + iter = find_if(objectList.begin(), objectList.end(), + ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0))); + if ( iter == objectList.end() ) { // We failed to find a cert with a matching CKA_ID. This // can happen if the cert is not present on the token, or // the der encoded cert stored on the token was corrupted. - throw PKCS11Exception(CKR_DEVICE_ERROR, - "Failed to find cert with matching CKA_ID value"); - } - keyObj.completeKey(*iter); + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Failed to find cert with matching CKA_ID value"); + } + keyObj.completeKey(*iter); + + /* For now this is how we determine what type of key. + Also for now, allow only one or the other */ + if ( keyObj.getKeyType() == PKCS11Object::ecc) { + mECC = true; + } else { + mECC = false; + } + } objectList.push_back(keyObj); @@ -1577,6 +1758,7 @@ Slot::addCertObject(list& void Slot::unloadObjects() { + mECC = false; tokenObjects.clear(); free(personName); personName = NULL; @@ -1970,7 +2152,7 @@ Slot::readCUID(void) // shared memory is protected by our transaction call on the card // CKYStatus status; - if (state & CAC_CARD) { + if (state & GOV_CARD) { status = CACApplet_SelectCardManager(conn, NULL); } else { status = CKYApplet_SelectCardManager(conn, NULL); @@ -2203,6 +2385,50 @@ Slot::fetchCombinedObjects(const CKYBuff return objInfoList; } +typedef enum { + BER_UNWRAP, + BER_NEXT +} BERop; + +static CKYStatus +berProcess(CKYBuffer *buf, int matchTag, CKYBuffer *target, BERop type) +{ + unsigned char tag; + unsigned int used_length= 0; + unsigned int data_length; + + tag = CKYBuffer_GetChar(buf,used_length++); + + /* blow out when we come to the end */ + if (matchTag && tag != matchTag) { + return CKYLIBFAIL; + } + + data_length = CKYBuffer_GetChar(buf,used_length++); + + if (data_length & 0x80) { + int len_count = data_length & 0x7f; + + data_length = 0; + + while (len_count-- > 0) { + data_length = (data_length << 8) | + CKYBuffer_GetChar(buf,used_length++); + } + } + + if (data_length > (CKYBuffer_Size(buf)-used_length) ) { + return CKYLIBFAIL; + } + + if (type == BER_UNWRAP) { + return CKYBuffer_AppendBuffer(target, buf, used_length, data_length); + } + return CKYBuffer_AppendBuffer(target, buf, used_length+data_length, + CKYBuffer_Size(buf)-(used_length+data_length)); +} + + CKYStatus Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, bool throwException) @@ -2211,16 +2437,60 @@ Slot::readCACCertificateFirst(CKYBuffer CKYISOStatus apduRC; *nextSize = 0; + if (state & PIV_CARD) { + CKYBuffer pivData; + CKYBuffer certInfo; + + CKYBuffer_InitEmpty(&pivData); + CKYBuffer_InitEmpty(&certInfo); + CKYBuffer_Resize(cert, 0); + status = PIVApplet_GetCertificate(conn, cert, pivContainer, &apduRC); + if (throwException && (status != CKYSUCCESS)) { + handleConnectionError(); + } + /* actually, on success, we need to parse the certificate and find the + * propper tag */ + if (status == CKYSUCCESS) { + status = berProcess(cert, 0x53, &pivData, BER_UNWRAP); + CKYBuffer_Resize(cert, 0); + CKYBuffer_AppendChar(cert,0); + do { + CKYByte tag = CKYBuffer_GetChar(&pivData,0); + if (tag == CAC_TAG_CERTIFICATE) { + status = berProcess(&pivData, CAC_TAG_CERTIFICATE, + cert, BER_UNWRAP); + } + if (tag == CAC_TAG_CERTINFO) { + CKYBuffer_Resize(&certInfo, 0); + status = berProcess(&pivData, CAC_TAG_CERTINFO, + &certInfo, BER_UNWRAP); + if (CKYBuffer_Size(&certInfo) == 1) { + CKYBuffer_SetChar(cert,0, + CKYBuffer_GetChar(&certInfo,0)); + } + } + if (status == CKYSUCCESS) { + CKYBuffer_Resize(&certInfo, 0); + status = berProcess(&pivData, 0, &certInfo, BER_NEXT); + if (status == CKYSUCCESS) { + CKYBuffer_Resize(&pivData,0); + status = CKYBuffer_AppendCopy(&pivData,&certInfo); + } + } + } while ((status == CKYSUCCESS) && (CKYBuffer_Size(&pivData) != 0)); + CKYBuffer_FreeData(&pivData); + CKYBuffer_FreeData(&certInfo); + } + + return status; + } + if (mOldCAC) { /* get the first 100 bytes of the cert */ status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC); if (throwException && (status != CKYSUCCESS)) { handleConnectionError(); } - - if(CKYBuffer_Size(cert) == 0) { - handleConnectionError(); - } return status; } @@ -2233,6 +2503,7 @@ Slot::readCACCertificateFirst(CKYBuffer CKYBuffer_InitEmpty(&tBuf); CKYBuffer_InitEmpty(&vBuf); CKYBuffer_Resize(cert, 0); + CKYBuffer_AppendChar(cert,0); /* handle the new CAC card read */ /* read the TLV */ @@ -2258,11 +2529,12 @@ Slot::readCACCertificateFirst(CKYBuffer length = CKYBuffer_GetShortLE(&tBuf, toffset); toffset +=2; } - if (tag != CAC_TAG_CERTIFICATE) { - continue; + if (tag == CAC_TAG_CERTIFICATE) { + CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length); + } + if (tag == CAC_TAG_CERTINFO) { + CKYBuffer_SetChar(cert,0,CKYBuffer_GetChar(&vBuf,voffset)); } - CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length); - break; } status = CKYSUCCESS; @@ -2272,6 +2544,191 @@ done: return status; } + +const static unsigned long crc_table[] = { +0x00000000,0x77073096,0xee0e612c,0x990951ba, +0x076dc419,0x706af48f,0xe963a535,0x9e6495a3, +0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988, +0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91, +0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de, +0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7, +0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, +0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5, +0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172, +0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b, +0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940, +0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59, +0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116, +0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, +0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924, +0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d, +0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a, +0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433, +0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818, +0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01, +0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, +0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457, +0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c, +0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65, +0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2, +0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb, +0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0, +0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, +0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086, +0x5768b525,0x206f85b3,0xb966d409,0xce61e49f, +0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4, +0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad, +0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a, +0xead54739,0x9dd277af,0x04db2615,0x73dc1683, +0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, +0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1, +0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe, +0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7, +0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc, +0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5, +0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252, +0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, +0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60, +0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79, +0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236, +0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f, +0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04, +0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d, +0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, +0x9c0906a9,0xeb0e363f,0x72076785,0x05005713, +0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38, +0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21, +0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e, +0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777, +0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c, +0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, +0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2, +0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db, +0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0, +0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9, +0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6, +0xbad03605,0xcdd70693,0x54de5729,0x23d967bf, +0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, +0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d +}; + +static unsigned long +calc_crc32(const unsigned char *buf, int len) +{ + unsigned long crc = 0xffffffff; + int i; + + for (i=0; i < len; i++) { + unsigned char crc_low = crc & 0xff; + unsigned long crc_high = crc >> 8; + crc = crc_table[crc_low ^ buf[i]] ^ crc_high; + } + return crc ^ 0xffffffff; +} + +/* + * decompress, handles both gzip and zlib trailers + * it also automatically allocates the output buffer and expands it as + * necessary. + */ +static int +decompress(CKYBuffer *out, + CKYBuffer *in, CKYOffset offset, CKYSize len) +{ + int zret; + CKYStatus status; + z_stream stream; + int chunk = len *2; + int outlen = 0; + + + /* allocate inflate state */ + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.avail_in = 0; + stream.next_in = Z_NULL; + zret = inflateInit(&stream); + if (zret != Z_OK) + return zret; + + status = CKYBuffer_Reserve(out, outlen); + if (status != CKYSUCCESS) { + return Z_MEM_ERROR; + } + + stream.avail_in = len; + stream.next_in = (Bytef *)(CKYBuffer_Data(in) + offset); + + do { + CKYBuffer_Resize(out, outlen + chunk); + stream.avail_out = chunk; + + stream.next_out = (Bytef *)CKYBuffer_Data(out)+ outlen; + + zret= inflate(&stream, Z_NO_FLUSH); + + /* we need the length early so it can be used in error processing */ + outlen += chunk - stream.avail_out; + + /* proccess the error codes */ + switch (zret) { + case Z_DATA_ERROR: + /* a DATA error can occur on either corrupted data, or on gzip. + * data. This is because gzip uses CRC32 and zlib used ADLER32 + * checksums. We need to check to see if this failure is do to + * a gzip header. */ + /* 1) a gzip header includes 4 extra bytes containing the length + * of the gziped data. This means there must be 4 more bytes + * in our input buffer that have not been processed */ + if (stream.avail_in != 4) { + break; /* not a gzip header */ + } + /* The last 4 bytes of a gzip header include the uncompressed length + * modulo 2^32. Make sure the actual uncompressed length matches + * the header. */ + if ((outlen & 0xffffffffL) + != CKYBuffer_GetLongLE(in, offset+len-4)) { + break; /* didn't decode the full length */ + } + /* At this point it''s pretty likely we have a gzip trailer. Verify + * the crc32 values to make sure there hasn't been any corruption. + */ + if (calc_crc32(CKYBuffer_Data(out), outlen) != + CKYBuffer_GetLongLE(in,offset+len-8)) { + break; /* CRC didn't match */ + } + /* This was valid gzip data, and we've successfully uncompressed + * it. We're now done. */ + zret=Z_STREAM_END; + break; + case Z_NEED_DICT: + /* if we need the dict, it wasn't in the data, + * so it's a data error */ + zret = Z_DATA_ERROR; + break; + case Z_OK: + /* Z_OK means we need more data, expand the buffer and go again. + * if we don't need more buffer space, then the input must have + * been truncated, that's a data error */ + if (stream.avail_out != 0) { + zret = Z_DATA_ERROR; + } + break; + } + } while (zret == Z_OK); + + /* cleanup */ + if (zret == Z_STREAM_END) { + zret = Z_OK; + CKYBuffer_Resize(out, outlen); + } else { + CKYBuffer_Resize(out, 0); + } + (void)inflateEnd(&stream); + return zret; +} + /* * only necessary for old CAC cards. New CAC cards have to read the * whole cert in anyway above.... @@ -2304,7 +2761,7 @@ Slot::loadCACCert(CKYByte instance) // catch the applet selection errors if they don't // try { - selectCACApplet(instance); + selectCACApplet(instance, false); } catch(PKCS11Exception& e) { // all CAC's must have instance '0', throw the error it // they don't. @@ -2322,6 +2779,10 @@ Slot::loadCACCert(CKYByte instance) if (instance == 0) { readCACCertificateFirst(&rawCert, &nextSize, true); + + if(CKYBuffer_Size(&rawCert) <= 1) { + handleConnectionError(); + } log->log("CAC Cert %d: fetch CAC Cert: %d ms\n", instance, OSTimeNow() - time); } @@ -2364,7 +2825,7 @@ Slot::loadCACCert(CKYByte instance) } else { status = readCACCertificateFirst(&rawCert, &nextSize, false); - if (status != CKYSUCCESS) { + if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) { /* CAC only requires the Certificate in pki '0' */ /* if pki '1' or '2' are empty, treat it as a non-fatal error*/ if (instance == 2) { @@ -2393,31 +2854,61 @@ Slot::loadCACCert(CKYByte instance) log->log("CAC Cert %d: Cert has been read: %d ms\n", instance, OSTimeNow() - time); - if (!mOldCAC || CKYBuffer_GetChar(&rawCert,0) == 1) { - CKYSize guessFinalSize = CKYBuffer_Size(&rawCert); - CKYSize certSize = 0; - CKYOffset offset = mOldCAC ? 1 : 0; + /* new CACs, and old CACs with the high one bit are compressed, + * uncompress them */ + if ((CKYBuffer_GetChar(&rawCert,0) & 0x3) == 1) { + CKYOffset offset = 1; int zret = Z_MEM_ERROR; - do { - guessFinalSize *= 2; - status = CKYBuffer_Resize(&cert, guessFinalSize); - if (status != CKYSUCCESS) { - break; + /* process the GZIP header if present */ + /* header_id = 0x1f, 0x8b. CM=8. If we ever support something other + * than CM=8, we need to change the zlib header below. Currently both + * gzip and zlib only support CM=8 (DEFLATE) compression */ + if ((CKYBuffer_GetChar(&rawCert,1) == 0x1f) && + (CKYBuffer_GetChar(&rawCert,2) == 0x8b) && + (CKYBuffer_GetChar(&rawCert,3) == 8)) { + CKYByte flags = CKYBuffer_GetChar(&rawCert,4); + /* this has a gzip header, not raw data. */ + offset += 10; /* base size of the gzip header */ + if (flags & 4) { /* FEXTRA */ + CKYSize len = CKYBuffer_GetShortLE(&rawCert,offset); + offset += len; } - certSize = guessFinalSize; - zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize, - CKYBuffer_Data(&rawCert)+offset, - CKYBuffer_Size(&rawCert)-offset); - } while (zret == Z_BUF_ERROR); + if (flags & 8) { /* FNAME */ + while (CKYBuffer_GetChar(&rawCert,offset) != 0) { + offset++; + } + offset++; + } + if (flags & 0x10) { /* FComment */ + while (CKYBuffer_GetChar(&rawCert,offset) != 0) { + offset++; + } + offset++; + } + if (flags & 2) { /* FHCRC */ + offset += 2; + } + offset -= 2; + + /* add zlib header, so libz will be happy */ + /* CINFO=7, CM=8, LEVEL=2, DICTFLAG=0, FCHECK= 1c */ + /* NOTE: the zlib will fail when procssing the trailer. this is + * ok because decompress automatically notices the failure and + * and checks the gzip trailer. */ + CKYBuffer_SetChar(&rawCert, offset, 0x78); + CKYBuffer_SetChar(&rawCert, offset+1, 0x9c); + } + /* uncompress. This expands cert as necessary. */ + zret = decompress(&cert, &rawCert, offset, + CKYBuffer_Size(&rawCert)-offset); if (zret != Z_OK) { CKYBuffer_FreeData(&rawCert); CKYBuffer_FreeData(&cert); throw PKCS11Exception(CKR_DEVICE_ERROR, - "Corrupted compressed CAC Cert"); + "Corrupted compressed CAC/PIV Cert"); } - CKYBuffer_Resize(&cert,certSize); } else { CKYBuffer_InitFromBuffer(&cert,&rawCert,1,CKYBuffer_Size(&rawCert)-1); } @@ -2431,6 +2922,9 @@ Slot::loadCACCert(CKYByte instance) tokenObjects.push_back(privKey); tokenObjects.push_back(pubKey); tokenObjects.push_back(certObj); + if ( pubKey.getKeyType() == PKCS11Object::ecc) { + mECC = 1; + } if (personName == NULL) { const char *name = certObj.getName(); @@ -2459,7 +2953,7 @@ Slot::loadObjects() list objInfoList; std::list::iterator iter; - if (state & CAC_CARD) { + if (state & GOV_CARD) { loadCACCert(0); loadCACCert(1); loadCACCert(2); @@ -2704,6 +3198,7 @@ Slot::login(SessionHandleSuffix handleSu } if (!isVersion1Key) { + pinCache.invalidate(); pinCache.set((const char *)pPin, ulPinLen); } else if (nonceValid) { throw PKCS11Exception(CKR_USER_ALREADY_LOGGED_IN); @@ -2713,15 +3208,15 @@ Slot::login(SessionHandleSuffix handleSu CKYStatus status = trans.begin(conn); if(status != CKYSUCCESS ) handleConnectionError(); - if (state & CAC_CARD) { - selectCACApplet(0); + if (state & GOV_CARD) { + selectCACApplet(0, true); } else { selectApplet(); } if (isVersion1Key) { attemptLogin((const char *)pPin); - } else if (state & CAC_CARD) { + } else if (state & GOV_CARD) { attemptCACLogin(); } else { oldAttemptLogin(); @@ -2738,7 +3233,8 @@ Slot::attemptCACLogin() CKYISOStatus result; status = CACApplet_VerifyPIN(conn, - (const char *)CKYBuffer_Data(pinCache.get()), &result); + (const char *)CKYBuffer_Data(pinCache.get()), + mCACLocalLogin, &result); if( status == CKYSCARDERR ) { handleConnectionError(); } @@ -2746,8 +3242,10 @@ Slot::attemptCACLogin() case CKYISO_SUCCESS: break; case 0x6981: + pinCache.clearPin(); throw PKCS11Exception(CKR_PIN_LOCKED); default: + pinCache.clearPin(); if ((result & 0xff00) == 0x6300) { throw PKCS11Exception(CKR_PIN_INCORRECT); } @@ -2776,10 +3274,13 @@ Slot::oldAttemptLogin() case CKYISO_SUCCESS: break; case CKYISO_AUTH_FAILED: + pinCache.clearPin(); throw PKCS11Exception(CKR_PIN_INCORRECT); case CKYISO_IDENTITY_BLOCKED: + pinCache.clearPin(); throw PKCS11Exception(CKR_PIN_LOCKED); default: + pinCache.clearPin(); throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet returned 0x%04x", result); } @@ -2866,7 +3367,7 @@ Slot::logout(SessionHandleSuffix suffix) throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); } - if (state & CAC_CARD) { + if (state & GOV_CARD) { CACLogout(); return; } @@ -2993,7 +3494,7 @@ Slot::getAttributeValue(SessionHandleSuf ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(), ObjectHandleMatch(hObject)); - if( iter == tokenObjects.end() ) { + if ( iter == tokenObjects.end()) { throw PKCS11Exception(CKR_OBJECT_HANDLE_INVALID); } @@ -3077,6 +3578,21 @@ SlotList::generateRandom(CK_SESSION_HAND } void +SlotList::derive(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) +{ + + CK_SLOT_ID slotID; + SessionHandleSuffix suffix; + + decomposeSessionHandle(hSession, slotID, suffix); + + slots[slotIDToIndex(slotID)]->derive(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey); + +} + +void Slot::ensureValidSession(SessionHandleSuffix suffix) { if( ! isValidSession(suffix) ) { @@ -3110,6 +3626,23 @@ Slot::objectHandleToKeyNum(CK_OBJECT_HAN return keyNum & 0xFF; } +PKCS11Object::KeyType +Slot::getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey) +{ + ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(), + ObjectHandleMatch(hKey)); + + if( iter == tokenObjects.end() ) { + throw PKCS11Exception(CKR_KEY_HANDLE_INVALID); + } + + if( getObjectClass(iter->getMuscleObjID()) != 'k' ) { + throw PKCS11Exception(CKR_KEY_HANDLE_INVALID); + } + + return iter->getKeyType(); +} + void Slot::signInit(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) @@ -3119,7 +3652,10 @@ Slot::signInit(SessionHandleSuffix suffi if( session == sessions.end() ) { throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); } - session->signatureState.initialize(objectHandleToKeyNum(hKey)); + + PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey); + + session->signatureState.initialize(objectHandleToKeyNum(hKey), keyType); } void @@ -3131,7 +3667,10 @@ Slot::decryptInit(SessionHandleSuffix su if( session == sessions.end() ) { throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); } - session->decryptionState.initialize(objectHandleToKeyNum(hKey)); + + PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey); + + session->decryptionState.initialize(objectHandleToKeyNum(hKey), keyType); } /** @@ -3240,54 +3779,141 @@ stripRSAPadding(CKYBuffer *stripped, con } } -class RSASignatureParams : public CryptParams { +class ECCKeyAgreementParams : public CryptParams { public: - RSASignatureParams(unsigned int keysize) : CryptParams(keysize) { } + ECCKeyAgreementParams(unsigned int keysize) : CryptParams(keysize) { } - CKYByte getDirection() const { return CKY_DIR_ENCRYPT; } + CKYByte getDirection() const { return CKY_DIR_NONE;} CryptOpState& getOpState(Session& session) const { - return session.signatureState; + return session.keyAgreementState; } void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { - // RSA_NO_PAD requires RSA PKCS #1 Type 1 padding - CKYStatus status = CKYBuffer_Resize(paddedInput,getKeySize()/8); - if (status != CKYSUCCESS) { - throw PKCS11Exception(CKR_HOST_MEMORY); - } - padRSAType1(unpaddedInput, paddedInput); return; } void unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { - // no need to unpad ciphertext - CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(paddedOutput), - CKYBuffer_Size(paddedOutput)); - + return; } + }; -class RSADecryptParams: public CryptParams { +class SignatureParams : public CryptParams { public: - RSADecryptParams(unsigned int keysize) : CryptParams(keysize) { } + SignatureParams(unsigned int keysize) : CryptParams(keysize) { } - CKYByte getDirection() const { return CKY_DIR_DECRYPT; } + CKYByte getDirection() const { return CKY_DIR_NONE; } CryptOpState& getOpState(Session& session) const { - return session.decryptionState; + return session.signatureState; } void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { - // no need to unpad ciphertext - CKYBuffer_Replace(paddedInput, 0, CKYBuffer_Data(unpaddedInput), - CKYBuffer_Size(unpaddedInput)); + return; } void unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { - // strip off PKCS #1 padding + return; + } + +}; + + + +class ECCSignatureParams : public CryptParams { + public: + ECCSignatureParams(unsigned int keysize) : CryptParams(keysize) { } + + CKYByte getDirection() const { return CKY_DIR_NONE; } + + CryptOpState& getOpState(Session& session) const { + return session.signatureState; + } + + void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { + return; + } + + void + unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { + /* Here we will unpack the DER encoding of the signature */ + + if ( unpaddedOutput == NULL || paddedOutput == NULL) { + throw PKCS11Exception(CKR_ARGUMENTS_BAD); + } + + CKYBuffer rawSignature; + CKYBuffer_InitEmpty(&rawSignature); + + DEREncodedSignature sig(paddedOutput); + + int rv = sig.getRawSignature(&rawSignature, getKeySize() ); + + if (rv == CKYSUCCESS) { + CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(&rawSignature), + CKYBuffer_Size(&rawSignature)); + } else { + throw PKCS11Exception(CKR_DEVICE_ERROR); + } + + CKYBuffer_FreeData(&rawSignature); + + } + +}; + + +class RSASignatureParams : public CryptParams { + public: + RSASignatureParams(unsigned int keysize) : CryptParams(keysize) { } + + CKYByte getDirection() const { return CKY_DIR_ENCRYPT; } + + CryptOpState& getOpState(Session& session) const { + return session.signatureState; + } + + void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { + // RSA_NO_PAD requires RSA PKCS #1 Type 1 padding + CKYStatus status = CKYBuffer_Resize(paddedInput,getKeySize()/8); + if (status != CKYSUCCESS) { + throw PKCS11Exception(CKR_HOST_MEMORY); + } + padRSAType1(unpaddedInput, paddedInput); + return; + } + + void + unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { + // no need to unpad ciphertext + CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(paddedOutput), + CKYBuffer_Size(paddedOutput)); + + } +}; + +class RSADecryptParams: public CryptParams { + public: + RSADecryptParams(unsigned int keysize) : CryptParams(keysize) { } + + CKYByte getDirection() const { return CKY_DIR_DECRYPT; } + + CryptOpState& getOpState(Session& session) const { + return session.decryptionState; + } + + void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { + // no need to unpad ciphertext + CKYBuffer_Replace(paddedInput, 0, CKYBuffer_Data(unpaddedInput), + CKYBuffer_Size(unpaddedInput)); + } + + void + unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { + // strip off PKCS #1 padding stripRSAPadding( unpaddedOutput, paddedOutput ); return; } @@ -3298,9 +3924,38 @@ Slot::sign(SessionHandleSuffix suffix, C CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { - RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE); - cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen, - params); + + refreshTokenState(); + SessionIter session = findSession(suffix); + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } + + if (!isVersion1Key && ! isLoggedIn() ) { + throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN); + } + + /* Create a default one just to get the sigState */ + SignatureParams dummyParams(CryptParams::DEFAULT_KEY_SIZE); + + CryptOpState sigState = dummyParams.getOpState(*session); + + PKCS11Object::KeyType keyType = sigState.keyType; + + if ( keyType == PKCS11Object::unknown) { + throw PKCS11Exception(CKR_DATA_INVALID); + } + + if( keyType == Key::ecc ) { + ECCSignatureParams params(CryptParams::ECC_DEFAULT_KEY_SIZE); + signECC(suffix, pData, ulDataLen, pSignature, pulSignatureLen, + params); + + } else if (keyType == Key::rsa) { + RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE); + cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen, + params); + } } void @@ -3334,9 +3989,9 @@ Slot::cryptRSA(SessionHandleSuffix suffi CKYBuffer *result = &opState.result; CKYByte keyNum = opState.keyNum; - unsigned int keySize = getKeySize(keyNum); + unsigned int keySize = getRSAKeySize(keyNum); - if(keySize != CryptParams::DEFAULT_KEY_SIZE) + if (keySize != CryptParams::DEFAULT_KEY_SIZE) params.setKeySize(keySize); if( CKYBuffer_Size(result) == 0 ) { @@ -3358,7 +4013,8 @@ Slot::cryptRSA(SessionHandleSuffix suffi } try { params.padInput(&inputPad, &input); - performRSAOp(&output, &inputPad, keyNum, params.getDirection()); + performRSAOp(&output, &inputPad, params.getKeySize(), + keyNum, params.getDirection()); params.unpadOutput(result, &output); CKYBuffer_FreeData(&input); CKYBuffer_FreeData(&inputPad); @@ -3395,10 +4051,166 @@ Slot::getNonce() return &nonce; } +void Slot::signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput, + CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, + CK_ULONG_PTR pulOutputLen, CryptParams& params) +{ + + if( pulOutputLen == NULL ) { + throw PKCS11Exception(CKR_DATA_INVALID, + "output length is NULL"); + } + + refreshTokenState(); + SessionIter session = findSession(suffix); + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } + /* version 1 keys may not need login. We catch the error + on the operation. The token will not allow us to sign with + a protected key unless we are logged in. + can be removed when version 0 support is depricated. + */ + + if (!isVersion1Key && ! isLoggedIn() ) { + throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN); + } + CryptOpState& opState = params.getOpState(*session); + CKYBuffer *result = &opState.result; + CKYByte keyNum = opState.keyNum; + + unsigned int keySize = getECCKeySize(keyNum); + + if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE) + params.setKeySize(keySize); + + if( CKYBuffer_Size(result) == 0 ) { + unsigned int maxSize = params.getKeySize()/8; + + if( pInput == NULL || ulInputLen == 0) { + throw PKCS11Exception(CKR_DATA_LEN_RANGE); + } + if (ulInputLen > maxSize) { + //pInput += ulInputLen - maxSize; + ulInputLen = maxSize; + } + + CKYBuffer input; + CKYBuffer output; + CKYBuffer_InitEmpty(&output); + CKYStatus status = CKYBuffer_InitFromData(&input, pInput, ulInputLen); + + if (status != CKYSUCCESS) { + CKYBuffer_FreeData(&output); + throw PKCS11Exception(CKR_HOST_MEMORY); + } + try { + performECCSignature(&output, &input, params.getKeySize(), keyNum); + params.unpadOutput(result, &output); + CKYBuffer_FreeData(&input); + CKYBuffer_FreeData(&output); + } catch(PKCS11Exception& e) { + CKYBuffer_FreeData(&input); + CKYBuffer_FreeData(&output); + throw(e); + } + } + + if( pOutput != NULL ) { + if( *pulOutputLen < CKYBuffer_Size(result) ) { + *pulOutputLen = CKYBuffer_Size(result); + throw PKCS11Exception(CKR_BUFFER_TOO_SMALL); + } + memcpy(pOutput, CKYBuffer_Data(result), CKYBuffer_Size(result)); + } + *pulOutputLen = CKYBuffer_Size(result); +} + +void +Slot::performECCSignature(CKYBuffer *output, const CKYBuffer *input, + unsigned int keySize, CKYByte keyNum) +{ + + /* establish a transaction */ + Transaction trans; + CKYStatus status = trans.begin(conn); + if( status != CKYSUCCESS ) handleConnectionError(); + + if (!mECC) { + throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); + } + + if (state & GOV_CARD) { + selectCACApplet(keyNum, true); + } else { + selectApplet(); + } + + CKYISOStatus result; + int loginAttempted = 0; + +retry: + if (state & PIV_CARD) { + status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result); + } else if (state & CAC_CARD) { + status = CACApplet_SignDecrypt(conn, input, output, &result); + } else { + status = CKYApplet_ComputeECCSignature(conn, keyNum, input, NULL, output, getNonce(), &result); + } + /* map the ISO not logged in code to the coolkey one */ + if ((result == CKYISO_CONDITION_NOT_SATISFIED) || + (result == CKYISO_SECURITY_NOT_SATISFIED)) { + result = (CKYStatus) CKYISO_UNAUTHORIZED; + } + + if (status != CKYSUCCESS) { + if ( status == CKYSCARDERR ) { + handleConnectionError(); + } + + if (result == CKYISO_DATA_INVALID) { + throw PKCS11Exception(CKR_DATA_INVALID); + } + /* version0 keys could be logged out in the middle by someone else, + reauthenticate... This code can go away when we depricate. + version0 applets. + */ + if (!isVersion1Key && !loginAttempted && + (result == CKYISO_UNAUTHORIZED)) { + /* try to reauthenticate */ + try { + if (state & GOV_CARD) { + attemptCACLogin(); + } else { + oldAttemptLogin(); + } + } catch(PKCS11Exception& ) { + /* attemptLogin can throw things like CKR_PIN_INCORRECT + that don't make sense from a crypto operation. This is + a result of pin caching. We will reformat any login + exception to a CKR_DEVICE_ERROR. + */ + throw PKCS11Exception(CKR_DEVICE_ERROR); + } + loginAttempted = true; + goto retry; /* easier to understand than a while loop in this case. */ + } + throw PKCS11Exception( result == CKYISO_UNAUTHORIZED ? + CKR_USER_NOT_LOGGED_IN : CKR_DEVICE_ERROR); + + } + +} + + void -Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, +Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, unsigned int keySize, CKYByte keyNum, CKYByte direction) { + if ( mECC ) { + throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); + } + // // establish a transaction // @@ -3409,8 +4221,8 @@ Slot::performRSAOp(CKYBuffer *output, co // // select the applet // - if (state & CAC_CARD) { - selectCACApplet(keyNum); + if (state & GOV_CARD) { + selectCACApplet(keyNum, true); } else { selectApplet(); } @@ -3418,12 +4230,21 @@ Slot::performRSAOp(CKYBuffer *output, co CKYISOStatus result; int loginAttempted = 0; retry: - if (state & CAC_CARD) { + if (state & PIV_CARD) { + status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result); + } else if (state & CAC_CARD) { status = CACApplet_SignDecrypt(conn, input, output, &result); } else { status = CKYApplet_ComputeCrypt(conn, keyNum, CKY_RSA_NO_PAD, direction, input, NULL, output, getNonce(), &result); } + + /* map the ISO not logged in code to the coolkey one */ + if ((result == CKYISO_CONDITION_NOT_SATISFIED) || + (result == CKYISO_SECURITY_NOT_SATISFIED)) { + result = CKYISO_UNAUTHORIZED; + } + if (status != CKYSUCCESS) { if ( status == CKYSCARDERR ) { handleConnectionError(); @@ -3434,11 +4255,15 @@ retry: // version0 keys could be logged out in the middle by someone else, // reauthenticate... This code can go away when we depricate. // version0 applets. - if (!isVersion1Key && !loginAttempted && + if (!isVersion1Key && !loginAttempted && pinCache.isValid() && (result == CKYISO_UNAUTHORIZED)) { // try to reauthenticate try { - oldAttemptLogin(); + if (state & GOV_CARD) { + attemptCACLogin(); + } else { + oldAttemptLogin(); + } } catch(PKCS11Exception& ) { // attemptLogin can throw things like CKR_PIN_INCORRECT // that don't make sense from a crypto operation. This is @@ -3458,7 +4283,7 @@ void Slot::seedRandom(SessionHandleSuffix suffix, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { - if (state & CAC_CARD) { + if (state & GOV_CARD) { /* should throw unsupported */ throw PKCS11Exception(CKR_DEVICE_ERROR); } @@ -3510,7 +4335,7 @@ void Slot::generateRandom(SessionHandleSuffix suffix, const CK_BYTE_PTR pData, CK_ULONG ulDataLen) { - if (state & CAC_CARD) { + if (state & GOV_CARD) { /* should throw unsupported */ throw PKCS11Exception(CKR_DEVICE_ERROR); } @@ -3544,7 +4369,7 @@ Slot::generateRandom(SessionHandleSuffix #define MAX_NUM_KEYS 8 unsigned int -Slot::getKeySize(CKYByte keyNum) +Slot::getRSAKeySize(CKYByte keyNum) { unsigned int keySize = CryptParams::DEFAULT_KEY_SIZE; int modSize = 0; @@ -3574,3 +4399,238 @@ Slot::getKeySize(CKYByte keyNum) return keySize; } + +unsigned int +Slot::getECCKeySize(CKYByte keyNum) +{ + return calcECCKeySize(keyNum); +} + +unsigned int +Slot::calcECCKeySize(CKYByte keyNum) +{ + unsigned int keySize = CryptParams::ECC_DEFAULT_KEY_SIZE; + + if(keyNum >= MAX_NUM_KEYS) { + return keySize; + } + + ObjectConstIter iter; + iter = find_if(tokenObjects.begin(), tokenObjects.end(), + KeyNumMatch(keyNum,*this)); + + if( iter == tokenObjects.end() ) { + return keySize; + } + + CKYBuffer const *eccParams = iter->getAttribute(CKA_EC_PARAMS); + + if (eccParams == NULL) { + return keySize; + } + + /* Extract the oid from the params */ + + CKYByte ecParamsLen = CKYBuffer_GetChar(eccParams, 1); + + if ( ecParamsLen == 0 ) { + return keySize; + } + +/* Now compare against the limited known list of oid byte info */ + + unsigned int oidByteLen = 0; + + CKYByte curByte = 0; + + for (int i = 0 ; i < numECCurves ; i++ ) { + + oidByteLen = curveBytesNamePair[i].bytes[0]; + + if ( oidByteLen != (unsigned int ) ecParamsLen ) { + continue; + } + + int match = 1; + for ( int j = 0 ; j < ecParamsLen ; j++ ) { + curByte = CKYBuffer_GetChar(eccParams, 2 + j ); + if ( curveBytesNamePair[i].bytes[ j + 1 ] != curByte ) { + match = 0; + break; + } + } + + if ( match == 1 ) { + keySize = curveBytesNamePair[i].length; + return keySize; + } + + } + + return keySize; +} + +void +Slot::derive(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) +{ + + log->log("Inside of Slot::Derive! \n"); + + ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE); + SessionIter session = findSession(suffix); + + PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hBaseKey); + + session->keyAgreementState.initialize(objectHandleToKeyNum(hBaseKey), keyType); + deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey, params); + +} + +void Slot::deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params) +{ + if (pMechanism == NULL ) { + throw PKCS11Exception(CKR_ARGUMENTS_BAD); + } + + CK_ECDH1_DERIVE_PARAMS *mechParams = NULL; + + mechParams = (CK_ECDH1_DERIVE_PARAMS*) pMechanism->pParameter; + + if (mechParams == NULL || mechParams->kdf != CKD_NULL ) { + throw PKCS11Exception(CKR_ARGUMENTS_BAD); + } + + refreshTokenState(); + SessionIter session = findSession(suffix); + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } + + /* version 1 keys may not need login. We catch the error + on the operation. The token will not allow us to sign with + a protected key unless we are logged in. + can be removed when version 0 support is depricated. */ + + if (!isVersion1Key && ! isLoggedIn() ) { + throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN); + } + + CryptOpState& opState = params.getOpState(*session); + CKYBuffer *result = &opState.result; + CKYByte keyNum = opState.keyNum; + + unsigned int keySize = getECCKeySize(keyNum); + + if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE) + params.setKeySize(keySize); + + CK_MECHANISM_TYPE deriveMech = pMechanism->mechanism; + + CK_ULONG otherPublicLen = mechParams->ulPublicDataLen; + CK_BYTE_PTR otherPublicData = mechParams->pPublicData; + + CKYBuffer secretKeyBuffer; + CKYBuffer_InitEmpty(&secretKeyBuffer); + CKYBuffer publicDataBuffer; + CKYStatus status = CKYBuffer_InitFromData(&publicDataBuffer,otherPublicData, otherPublicLen); + + if (status != CKYSUCCESS) { + CKYBuffer_FreeData(&secretKeyBuffer); + throw PKCS11Exception(CKR_HOST_MEMORY); + } + + PKCS11Object *secret = NULL; + *phKey = 0; + + if( CKYBuffer_Size(result) == 0 ) { + try { + performECCKeyAgreement(deriveMech, &publicDataBuffer, &secretKeyBuffer, + keyNum, params.getKeySize()); + CK_OBJECT_HANDLE keyObjectHandle = generateUnusedObjectHandle(); + secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, pTemplate, ulAttributeCount); + } catch(PKCS11Exception& e) { + CKYBuffer_FreeData(&secretKeyBuffer); + CKYBuffer_FreeData(&publicDataBuffer); + throw(e); + } + } + + CKYBuffer_FreeData(&secretKeyBuffer); + CKYBuffer_FreeData(&publicDataBuffer); + + if ( secret ) { + + *phKey = secret->getHandle(); + delete secret; + } +} + +void +Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, CKYBuffer *publicDataBuffer, + CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize) +{ + if (!mECC) { + throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); + } + + Transaction trans; + CKYStatus status = trans.begin(conn); + if( status != CKYSUCCESS ) handleConnectionError(); + + if (state & GOV_CARD) { + selectCACApplet(keyNum, true); + } else { + selectApplet(); + } + + CKYISOStatus result; + int loginAttempted = 0; + +retry: + + if (state & PIV_CARD) { + status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, publicDataBuffer, + secretKeyBuffer, &result); + } else if (state & CAC_CARD) { + status = CACApplet_SignDecrypt(conn, publicDataBuffer, secretKeyBuffer, &result); + } else { + status = CKYApplet_ComputeECCKeyAgreement(conn, keyNum, + publicDataBuffer , NULL, secretKeyBuffer, getNonce(), &result); + } + /* map the ISO not logged in code to the coolkey one */ + if ((result == CKYISO_CONDITION_NOT_SATISFIED) || + (result == CKYISO_SECURITY_NOT_SATISFIED)) { + result = (CKYStatus) CKYISO_UNAUTHORIZED; + } + + if (status != CKYSUCCESS) { + if ( status == CKYSCARDERR ) { + handleConnectionError(); + } + + if (result == CKYISO_DATA_INVALID) { + throw PKCS11Exception(CKR_DATA_INVALID); + } + if (!isVersion1Key && !loginAttempted && + (result == CKYISO_UNAUTHORIZED)) { + try { + if (state & GOV_CARD) { + attemptCACLogin(); + } else { + oldAttemptLogin(); + } + } catch(PKCS11Exception& ) { + throw PKCS11Exception(CKR_DEVICE_ERROR); + } + loginAttempted = true; + goto retry; + } + + throw PKCS11Exception( result == CKYISO_UNAUTHORIZED ? + CKR_USER_NOT_LOGGED_IN : CKR_DEVICE_ERROR); + + } +} diff -up ./src/coolkey/slot.h.piv-ecc ./src/coolkey/slot.h --- ./src/coolkey/slot.h.piv-ecc 2013-09-08 15:50:33.098428320 -0700 +++ ./src/coolkey/slot.h 2013-09-08 15:50:33.125428774 -0700 @@ -211,24 +211,27 @@ class CryptOpState { State state; CKYByte keyNum; CKYBuffer result; + PKCS11Object::KeyType keyType; - CryptOpState() : state(NOT_INITIALIZED), keyNum(0) + CryptOpState() : state(NOT_INITIALIZED), keyNum(0), keyType(PKCS11Object::unknown) { CKYBuffer_InitEmpty(&result); } CryptOpState(const CryptOpState &cpy) : - state(cpy.state), keyNum(cpy.keyNum) { + state(cpy.state), keyNum(cpy.keyNum), keyType(cpy.keyType) { CKYBuffer_InitFromCopy(&result, &cpy.result); } CryptOpState &operator=(const CryptOpState &cpy) { state = cpy.state, keyNum = cpy.keyNum; + keyType = cpy.keyType; CKYBuffer_Replace(&result, 0, CKYBuffer_Data(&cpy.result), CKYBuffer_Size(&cpy.result)); return *this; } ~CryptOpState() { CKYBuffer_FreeData(&result); } - void initialize(CKYByte keyNum) { + void initialize(CKYByte keyNum, PKCS11Object::KeyType theKeyType) { state = IN_PROCESS; this->keyNum = keyNum; + this->keyType = theKeyType; CKYBuffer_Resize(&result, 0); } }; @@ -258,6 +261,7 @@ class Session { CryptOpState signatureState; CryptOpState decryptionState; + CryptOpState keyAgreementState; }; typedef list SessionList; @@ -267,12 +271,11 @@ typedef SessionList::const_iterator Sess class CryptParams { private: unsigned int keySize; // in bits - protected: - unsigned int getKeySize() const { return keySize; } public: // set the actual key size obtained from the card void setKeySize(unsigned int newKeySize) { keySize = newKeySize; } - enum { DEFAULT_KEY_SIZE = 1024 }; + unsigned int getKeySize() const { return keySize; } + enum { DEFAULT_KEY_SIZE = 1024, ECC_DEFAULT_KEY_SIZE=256 }; CryptParams(unsigned int keySize_) : keySize(keySize_) { } @@ -304,12 +307,15 @@ class Slot { ATR_MATCH = 0x04, APPLET_SELECTABLE = 0x08, APPLET_PERSONALIZED = 0x10, - CAC_CARD = 0x20 + CAC_CARD = 0x20, + PIV_CARD = 0x40 }; enum { NONCE_SIZE = 8 }; + static const SlotState GOV_CARD = (SlotState)(CAC_CARD|PIV_CARD); + private: Log *log; char *readerName; @@ -339,7 +345,10 @@ class Slot { bool fullTokenName; bool mCoolkey; bool mOldCAC; - + bool mCACLocalLogin; + int pivContainer; + int pivKey; + bool mECC; //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 }; #ifdef USE_SHMEM @@ -386,6 +395,7 @@ class Slot { const CKYBuffer *getATR(); bool isLoggedIn(); bool needLoggedIn(); + bool getPIVLoginType(); void testNonce(); void addKeyObject(list& objectList, @@ -395,6 +405,7 @@ class Slot { const CKYBuffer *derCert, CK_OBJECT_HANDLE handle); void addObject(list& objectList, const ListObjectInfo& info, CK_OBJECT_HANDLE handle); + PKCS11Object *createSecretKeyObject(CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer,CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount); void ensureValidSession(SessionHandleSuffix suffix); @@ -408,7 +419,7 @@ class Slot { CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize); void selectApplet(); - void selectCACApplet(CKYByte instance); + void selectCACApplet(CKYByte instance,bool do_disconnect); void unloadObjects(); void loadCACObjects(); void loadCACCert(CKYByte instance); @@ -432,12 +443,23 @@ class Slot { CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, CK_ULONG_PTR pulOutputLen, CryptParams& params); - void performRSAOp(CKYBuffer *out, const CKYBuffer *input, CKYByte keyNum, - CKYByte direction); + void performRSAOp(CKYBuffer *out, const CKYBuffer *input, unsigned int keySize, + CKYByte keyNum, CKYByte direction); + + void signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput, + CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, + CK_ULONG_PTR pulOutputLen, CryptParams& params); + + void performECCSignature(CKYBuffer *out, const CKYBuffer *input, + unsigned int keySize, CKYByte keyNum); + void performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, + CKYBuffer *publicDataBuffer, + CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize); void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu); CKYByte objectHandleToKeyNum(CK_OBJECT_HANDLE hKey); + unsigned int calcECCKeySize(CKYByte keyNum); Slot(const Slot &cpy) #ifdef USE_SHMEM : shmem(readerName) @@ -469,7 +491,10 @@ class Slot { } // actually get the size of a key in bits from the card - unsigned int getKeySize(CKYByte keyNum); + unsigned int getRSAKeySize(CKYByte keyNum); + unsigned int getECCKeySize(CKYByte keyNum); + + PKCS11Object::KeyType getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey); SessionHandleSuffix openSession(Session::Type type); void closeSession(SessionHandleSuffix handleSuffix); @@ -511,6 +536,16 @@ class Slot { CK_ULONG len); void generateRandom(SessionHandleSuffix suffix, CK_BYTE_PTR data, CK_ULONG len); + + void derive(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey); + + void deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params); + + bool getIsECC() { return mECC; } }; class SlotList { @@ -604,6 +639,10 @@ class SlotList { void seedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen); + void derive(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey); + }; #endif diff -up ./src/install/pk11install.c.piv-ecc ./src/install/pk11install.c --- ./src/install/pk11install.c.piv-ecc 2007-02-06 11:40:36.000000000 -0800 +++ ./src/install/pk11install.c 2013-09-08 15:50:33.126428790 -0700 @@ -171,7 +171,7 @@ int dirListCount = sizeof(dirList)/sizeo static void usage(char *prog) { - fprintf(stderr,"usage: %s [-u][-v] [-p path] module\n", prog); + fprintf(stderr,"usage: %s [-u][-v][-s][-l] [-p path] module\n", prog); return; } @@ -181,9 +181,9 @@ usage(char *prog) #define CONFIG_TAG "configDir=" int -installPKCS11(char *dirPath, InstType type, char *module) +installPKCS11(char *dirPath, char *dbType, InstType type, char *module) { - char *paramString = (char *)malloc(strlen(dirPath)+sizeof(CONFIG_TAG)+3); + char *paramString = (char *)malloc(strlen(dbType)+strlen(dirPath)+sizeof(CONFIG_TAG)+3); char *cp; char **rc; @@ -191,7 +191,7 @@ installPKCS11(char *dirPath, InstType ty PINST_SET_ERROR(ERROR_NOT_ENOUGH_MEMORY); return 0; } - sprintf(paramString,CONFIG_TAG"\"%s\" ",dirPath); + sprintf(paramString,CONFIG_TAG"\"%s%s\" ",dbType,dirPath); /* translate all the \'s to /'s */ for (cp=paramString; *cp; cp++) { @@ -214,7 +214,7 @@ installPKCS11(char *dirPath, InstType ty int -installAllPKCS11(char *dirPath, char *search, char *tail, +installAllPKCS11(char *dirPath, char *dbType, char *search, char *tail, InstType type, char *module) { char *searchString; @@ -282,9 +282,9 @@ installAllPKCS11(char *dirPath, char *se myPath=PINST_FULLPATH(tempPath,path); if (tail) { - installAllPKCS11(myPath, tail, NULL, type, module); + installAllPKCS11(myPath, dbType, tail, NULL, type, module); } else { - installPKCS11(myPath, type, module); + installPKCS11(myPath, dbType, type, module); } } while (PINST_NEXT(iter, fileData)); free(tempPath); @@ -309,6 +309,7 @@ int main(int argc, char **argv) int i; InstType type = Install; char * path = NULL; + char *dbType = ""; #ifdef WIN32 BOOL brc; HKEY regKey; @@ -333,6 +334,12 @@ int main(int argc, char **argv) case 'v': verbose = 1; break; + case 'l': + dbType = "dbm:"; + break; + case 's': + dbType = "sql:"; + break; case 'p': path = *argv++; if (path == NULL) { @@ -359,7 +366,7 @@ int main(int argc, char **argv) } if (path) { - installAllPKCS11(path, "", NULL, type, module); + installAllPKCS11(path, dbType, "", NULL, type, module); return 0; } @@ -444,7 +451,7 @@ int main(int argc, char **argv) if (!dirPath) { continue; } - installAllPKCS11(dirPath, dirList[i].search, dirList[i].tail, + installAllPKCS11(dirPath, dbType, dirList[i].search, dirList[i].tail, type, module); } diff -up ./src/libckyapplet/cky_applet.c.piv-ecc ./src/libckyapplet/cky_applet.c --- ./src/libckyapplet/cky_applet.c.piv-ecc 2013-09-08 15:50:33.099428337 -0700 +++ ./src/libckyapplet/cky_applet.c 2013-09-08 15:50:33.126428790 -0700 @@ -103,6 +103,22 @@ CKYAppletFactory_ComputeCryptOneStep(CKY } CKYStatus +CKYAppletFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, const void *param) +{ + const CKYAppletArgComputeECCSignature *ccs=(const CKYAppletArgComputeECCSignature *)param; + return CKYAPDUFactory_ComputeECCSignatureOneStep(apdu, ccs->keyNumber, + ccs->location, ccs->data, ccs->sig); +} + +CKYStatus +CKYAppletFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, const void *param) +{ + + const CKYAppletArgComputeECCKeyAgreement *ccs=(const CKYAppletArgComputeECCKeyAgreement *)param; + return CKYAPDUFactory_ComputeECCKeyAgreementOneStep(apdu, ccs->keyNumber, ccs->location, ccs->publicValue, ccs->secretKey); +} + +CKYStatus CKYAppletFactory_CreatePIN(CKYAPDU *apdu, const void *param) { const CKYAppletArgCreatePIN *cps = (const CKYAppletArgCreatePIN *)param; @@ -245,10 +261,25 @@ CACAppletFactory_SignDecryptFinal(CKYAPD } CKYStatus +PIVAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param) +{ + const PIVAppletArgSignDecrypt *psd = (const PIVAppletArgSignDecrypt *)param; + return PIVAPDUFactory_SignDecrypt(apdu, psd->chain, psd->alg, psd->key, + psd->len, psd->buf); +} + +CKYStatus CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) { const char *pin=(const char *)param; - return CACAPDUFactory_VerifyPIN(apdu, pin); + return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin); +} + +CKYStatus +PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) +{ + const char *pin=(const char *)param; + return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin); } CKYStatus @@ -259,6 +290,13 @@ CACAppletFactory_GetCertificate(CKYAPDU } CKYStatus +PIVAppletFactory_GetCertificate(CKYAPDU *apdu, const void *param) +{ + CKYBuffer *tag =(CKYBuffer*)param; + return PIVAPDUFactory_GetData(apdu, tag, 0); +} + +CKYStatus CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param) { const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param; @@ -325,6 +363,7 @@ CKYAppletFill_AppendBuffer(const CKYBuff CKYBuffer_Size(response) -2); } + CKYStatus CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param) { @@ -725,6 +764,32 @@ CKYApplet_ComputeCryptProcess(CKYCardCon &ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC); } +/* computeECCValue returns data in the form : + * len: short + * data: byte[len] + * This fill routine returns A buffer with a copy of data and a length of len */ +static CKYStatus +ckyAppletFill_ComputeECCValueFinal(const CKYBuffer *response, + CKYSize size, void *param) +{ + CKYBuffer *cbuf = (CKYBuffer *)param; + CKYSize respSize = CKYBuffer_Size(response); + CKYSize dataLen; + + if (cbuf == 0) { + return CKYSUCCESS; /* app didn't want the result */ + } + /* data response code + length code */ + if (respSize < 4) { + return CKYAPDUFAIL; + } + dataLen = CKYBuffer_GetShort(response, 0); + if (dataLen > (respSize-4)) { + return CKYAPDUFAIL; + } + return CKYBuffer_Replace(cbuf, 0, CKYBuffer_Data(response)+2, dataLen); +} + /* computeCrypt returns data in the form : * len: short * data: byte[len] @@ -872,6 +937,77 @@ fail: return ret; } +CKYStatus +CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber, + const CKYBuffer *publicValue, CKYBuffer *sharedSecret, + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) +{ + CKYStatus ret = CKYAPDUFAIL; + CKYAppletArgComputeECCKeyAgreement ccd; + CKYBuffer empty; + CKYISOStatus status; + /* Routine creates a sym key, should easily fit in one apdu */ + + CKYBuffer_InitEmpty(&empty); + ccd.keyNumber = keyNumber; + ccd.location = CKY_DL_APDU; + + if (!apduRC) + apduRC = &status; + + if (ccd.location == CKY_DL_APDU) { + ccd.publicValue = publicValue; + ccd.secretKey = sharedSecret; + ret = CKYApplet_HandleAPDU(conn, + CKYAppletFactory_ComputeECCKeyAgreementOneStep, &ccd, nonce, + CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal, + result, apduRC); + if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { + return ret; + } + } + + return ret; +} + +CKYStatus +CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber, + const CKYBuffer *data, CKYBuffer *sig, + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) +{ + int use2APDUs = 0; + int use_dl_object = 0; + short dataSize = 0; + CKYStatus ret = CKYAPDUFAIL; + CKYAppletArgComputeECCSignature ccd; + CKYBuffer empty; + CKYISOStatus status; + + CKYBuffer_InitEmpty(&empty); + ccd.keyNumber = keyNumber; + + /* Assume APDU, the signature can only get so big with our key sizes, ~ 130 for 521 bit key. */ + ccd.location = CKY_DL_APDU; + + if (!apduRC) + apduRC = &status; + + if (ccd.location == CKY_DL_APDU) { + ccd.data = data; + ccd.sig = sig; + ret = CKYApplet_HandleAPDU(conn, + CKYAppletFactory_ComputeECCSignatureOneStep, &ccd, nonce, + CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal, + result, apduRC); + if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { + return ret; + } + + } + + return ret; +} + /* * do a CAC Sign/Decrypt */ @@ -920,7 +1056,7 @@ done: * do a CAC VerifyPIN */ CKYStatus -CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, +CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local, CKYISOStatus *apduRC) { CKYStatus ret; @@ -929,7 +1065,7 @@ CACApplet_VerifyPIN(CKYCardConnection *c apduRC = &status; } - ret = CKYApplet_HandleAPDU(conn, + ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN : CACAppletFactory_VerifyPIN, pin, NULL, 0, CKYAppletFill_Null, NULL, apduRC); @@ -942,6 +1078,7 @@ CACApplet_VerifyPIN(CKYCardConnection *c return ret; } + /* * Get a CAC Certificate */ @@ -1078,6 +1215,278 @@ CACApplet_GetCertificateAppend(CKYCardCo return ret; } +/* Select the PIV applet */ +static CKYByte pivAid[] = {0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, + 0x10, 0x00}; +CKYStatus +PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC) +{ + CKYStatus ret; + CKYBuffer PIV_Applet_AID,return_AID; + + CKYBuffer_InitEmpty(&return_AID); + CKYBuffer_InitFromData(&PIV_Applet_AID, pivAid, sizeof(pivAid)); + ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, + &PIV_Applet_AID, + NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, + &return_AID, apduRC); + /* Some cards return OK, but don't switch to our applet */ + /* PIV has a well defined return for it's select, check to see if we have + * a PIV card here */ + if (CKYBuffer_GetChar(&return_AID,0) != 0x61) { + /* not an application property template, so not a PIV. We could + * check that the aid tag (0x4f) and theallocation authority tag (0x79) + * are present, but what we are really avoiding is broken cards that + * lie about being able to switch to a particular applet, so the first + * tag should be sufficient */ + ret = CKYAPDUFAIL; /* what we should have gotten */ + } + CKYBuffer_FreeData(&PIV_Applet_AID); + CKYBuffer_FreeData(&return_AID); + return ret; +} + +/* + * Get a PIV Certificate + */ +CKYStatus +PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, int tag, + CKYISOStatus *apduRC) +{ + CKYStatus ret; + CKYISOStatus status; + CKYBuffer tagBuf; + + CKYBuffer_InitEmpty(&tagBuf); + CKYBuffer_Reserve(&tagBuf,4); /* can be up to 4 bytes */ + + CKYBuffer_Resize(cert,0); + if (apduRC == NULL) { + apduRC = &status; + } + if (tag >= 0x01000000) { + ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 24) & 0xff); + if (ret != CKYSUCCESS) { goto loser; } + } + if (tag >= 0x010000) { + ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 16) & 0xff); + if (ret != CKYSUCCESS) { goto loser; } + } + if (tag >= 0x0100) { + ret =CKYBuffer_AppendChar(&tagBuf, (tag >> 8) & 0xff); + if (ret != CKYSUCCESS) { goto loser; } + } + ret = CKYBuffer_AppendChar(&tagBuf, tag & 0xff); + if (ret != CKYSUCCESS) { goto loser; } + + + ret = CKYApplet_HandleAPDU(conn, + PIVAppletFactory_GetCertificate, &tagBuf, NULL, + CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, cert, + apduRC); +loser: + CKYBuffer_FreeData(&tagBuf); + + return ret; +} + + +/* + * record the next ber tag and length. NOTE: this is a state machine. + * we can handle the case where we are passed the data just one byte + * at a time. + */ +static CKYStatus +pivUnwrap(const CKYBuffer *buf, CKYOffset *offset, + CKYSize *dataSize, PIVUnwrapState *unwrap) +{ + if (unwrap->tag == 0) { + unwrap->tag = CKYBuffer_GetChar(buf, *offset); + if (unwrap->tag == 0) unwrap->tag = 0xff; + (*offset)++; + (*dataSize)--; + } + if (*dataSize == 0) { + return CKYSUCCESS; + } + if (unwrap->length_bytes != 0) { + int len; + if (unwrap->length_bytes == -1) { + len = CKYBuffer_GetChar(buf, *offset); + unwrap->length_bytes = 0; + unwrap->length = len; + (*offset)++; + (*dataSize)--; + if (len & 0x80) { + unwrap->length = 0; + unwrap->length_bytes = len & 0x7f; + } + } + while ((*dataSize != 0) && (unwrap->length_bytes != 0)) { + len = CKYBuffer_GetChar(buf, *offset); + (*offset) ++; + (*dataSize) --; + unwrap->length = ((unwrap->length) << 8 | len); + unwrap->length_bytes--; + } + } + return CKYSUCCESS; +} + +/* + * Remove the BER wrapping first... + */ +static CKYStatus +pivAppletFill_AppendUnwrapBuffer(const CKYBuffer *response, + CKYSize size, void *param) +{ + PIVAppletRespSignDecrypt *prsd = (PIVAppletRespSignDecrypt *)param; + CKYBuffer *buf = prsd->buf; + CKYSize dataSize = CKYBuffer_Size(response); + CKYOffset offset = 0; + + if (dataSize <= 2) { + return CKYSUCCESS; + } + dataSize -= 2; + /* remove the first tag */ + (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_1); + if (dataSize == 0) { + return CKYSUCCESS; + } + /* remove the second tag */ + (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_2); + if (dataSize == 0) { + return CKYSUCCESS; + } + /* the rest is real data */ + return CKYBuffer_AppendData(buf, CKYBuffer_Data(response) + offset, + dataSize); +} + +static CKYStatus +piv_wrapEncodeLength(CKYBuffer *buf, int length, int ber_len) +{ + if (ber_len== 1) { + CKYBuffer_AppendChar(buf,length); + } else { + ber_len--; + CKYBuffer_AppendChar(buf,0x80+ber_len); + while(ber_len--) { + CKYBuffer_AppendChar(buf,(length >> (8*ber_len)) & 0xff); + } + } + return CKYSUCCESS; +} +/* + * do a PIV Sign/Decrypt + */ +CKYStatus +PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, unsigned int keySize, int derive, + const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC) +{ + CKYStatus ret; + CKYSize dataSize = CKYBuffer_Size(data); + CKYSize outputSize = keySize; + CKYOffset offset = 0; + CKYBuffer tmp; + CKYByte alg; + int ber_len_1; + int ber_len_2; + int length; + PIVAppletArgSignDecrypt pasd; + PIVAppletRespSignDecrypt prsd; + + /* PIV only defines RSA 1024 and 2048, ECC 256 and ECC 384!!! */ + if (keySize == 128) { /* 1024 bit == 128 bytes */ + ber_len_2 = 2; + ber_len_1 = 2; + alg = 0x6; + } else if (keySize == 256) { /* 2048 bits == 256 bytes */ + ber_len_2 = 3; + ber_len_1 = 3; + alg = 0x7; + } else if (keySize == 32) { /* 256 bits = 32 bytes */ + ber_len_2 = 1; + ber_len_1 = 1; + alg = 0x11; + if (!derive) outputSize = keySize*2; + } else if (keySize == 48) { /* 384 bits = 48 bytes */ + ber_len_2 = 1; + ber_len_1 = 1; + alg = 0x14; + if (!derive) outputSize = keySize*2; + } else { + return CKYINVALIDARGS; + } + + CKYBuffer_InitEmpty(&tmp); + ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE); + if (ret != CKYSUCCESS) { + goto done; + } + CKYBuffer_AppendChar(&tmp,0x7c); + piv_wrapEncodeLength(&tmp,dataSize + ber_len_2 + 3,ber_len_1); + CKYBuffer_AppendChar(&tmp,0x82); + CKYBuffer_AppendChar(&tmp,0x0); + CKYBuffer_AppendChar(&tmp, derive ? 0x85 : 0x81); + piv_wrapEncodeLength(&tmp,dataSize,ber_len_2); + + /* now length == header length from here to the end*/ + length = CKYBuffer_Size(&tmp); + + if (length + dataSize > CKY_MAX_WRITE_CHUNK_SIZE) { + CKYBuffer_AppendBuffer(&tmp, data, 0, CKY_MAX_WRITE_CHUNK_SIZE-length); + } else { + CKYBuffer_AppendBuffer(&tmp, data, 0, dataSize); + } + + prsd.tag_1.tag = 0; + prsd.tag_1.length_bytes = -1; + prsd.tag_1.length = 0; + prsd.tag_2.tag = 0; + prsd.tag_2.length_bytes = -1; + prsd.tag_2.length = 0; + prsd.buf = result; + pasd.alg = alg; + pasd.key = key; + pasd.buf = &tmp; + + CKYBuffer_Resize(result,0); + for(offset = -length; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; ) { + pasd.chain = 1; + pasd.len = 0; + ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, + &pasd, NULL, CKY_SIZE_UNKNOWN, + pivAppletFill_AppendUnwrapBuffer, + &prsd, apduRC); + if (ret != CKYSUCCESS) { + goto done; + } + CKYBuffer_Resize(&tmp,0); + /* increment before we append the next tmp buffer */ + offset += CKY_MAX_WRITE_CHUNK_SIZE; + CKYBuffer_AppendBuffer(&tmp, data, offset, + MIN(dataSize-offset, CKY_MAX_WRITE_CHUNK_SIZE)); + } + + pasd.chain = 0; + pasd.len = outputSize; + + ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, + &pasd, NULL, CKY_SIZE_UNKNOWN, + pivAppletFill_AppendUnwrapBuffer, + &prsd, apduRC); + + if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != outputSize)) { + /* RSA returns the same data size as input, didn't happen, so + * something is wrong. */ + } + +done: + CKYBuffer_FreeData(&tmp); + return ret; +} /* * PIN cluster diff -up ./src/libckyapplet/cky_applet.h.piv-ecc ./src/libckyapplet/cky_applet.h --- ./src/libckyapplet/cky_applet.h.piv-ecc 2013-09-08 15:50:33.099428337 -0700 +++ ./src/libckyapplet/cky_applet.h 2013-09-08 15:50:33.127428807 -0700 @@ -43,6 +43,8 @@ typedef unsigned short CKYISOStatus; /* #define CKYISO_MORE_MASK 0xff00 /* More data mask */ #define CKYISO_MORE 0x6300 /* More data available */ #define CKYISO_DATA_INVALID 0x6984 +#define CKYISO_CONDITION_NOT_SATISFIED 0x6985 /* AKA not logged in (CAC)*/ +#define CKYISO_SECURITY_NOT_SATISFIED 0x6982 /* AKA not logged in (PIV)*/ /* Applet Defined Return codes */ #define CKYISO_NO_MEMORY_LEFT 0x9c01 /* There have been memory * problems on the card */ @@ -78,6 +80,7 @@ typedef unsigned short CKYISOStatus; /* #define CAC_TAG_CARDURL 0xf3 #define CAC_TAG_CERTIFICATE 0x70 +#define CAC_TAG_CERTINFO 0x71 #define CAC_TLV_APP_PKI 0x04 /* @@ -218,12 +221,47 @@ typedef struct _CKYAppletArgComputeCrypt const CKYBuffer *sig; } CKYAppletArgComputeCrypt; +typedef struct _CKYAppletArgComputeECCSignature { + CKYByte keyNumber; + CKYByte location; + const CKYBuffer *data; + const CKYBuffer *sig; +} CKYAppletArgComputeECCSignature; + +typedef struct _CKYAppletArgComputeECCKeyAgreement { + CKYByte keyNumber; + CKYByte location; + const CKYBuffer *publicValue; + const CKYBuffer *secretKey; +} CKYAppletArgComputeECCKeyAgreement; + + typedef struct _CACAppletArgReadFile { CKYByte type; CKYByte count; unsigned short offset; } CACAppletArgReadFile; +typedef struct _PIVAppletArgSignDecrypt { + CKYByte alg; + CKYByte key; + CKYByte chain; + CKYSize len; + CKYBuffer *buf; +} PIVAppletArgSignDecrypt; + +typedef struct _pivUnwrapState { + CKYByte tag; + CKYByte length; + int length_bytes; +} PIVUnwrapState; + +typedef struct _PIVAppletRespSignDecrypt { + PIVUnwrapState tag_1; + PIVUnwrapState tag_2; + CKYBuffer *buf; +} PIVAppletRespSignDecrypt; + /* fills in an APDU from a structure -- form of all the generic factories*/ typedef CKYStatus (*CKYAppletFactory)(CKYAPDU *apdu, const void *param); /* fills in an a structure from a response -- form of all the fill structures*/ @@ -335,7 +373,6 @@ CKYStatus CKYAppletFill_AppendBuffer(con /* Single value fills: Byte, Short, & Long */ /* param == CKYByte * */ CKYStatus CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param); -/* param == CKYByte * */ CKYStatus CKYAppletFill_Short(const CKYBuffer *response, CKYSize size, void *param); CKYStatus CKYAppletFill_Long(const CKYBuffer *response, CKYSize size, void *param); @@ -361,7 +398,7 @@ CKYBool CKYApplet_VerifyResponse(const C * Sends the ADPU to the card through the connection conn. * Checks that the response was valid (returning the responce code in apduRC. * Formats the response data into fillArg with fillFunc - * nonce and apduRC can be NULL (no nonce is added, not status returned + * nonce and apduRC can be NULL (no nonce is added, no status returned * legal values for afArg are depened on afFunc. * legal values for fillArg are depened on fillFunc. */ @@ -377,7 +414,7 @@ CKYStatus CKYApplet_HandleAPDU(CKYCardCo * into function calls, with input and output parameters. * The application is still responsible for * 1) creating a connection to the card, - * 2) Getting a tranaction long, then + * 2) Getting a transaction lock, then * 3) selecting the appropriate applet (or Card manager). * Except for those calls that have been noted, the appropriate applet * is the CoolKey applet. @@ -490,9 +527,18 @@ CKYStatus CACApplet_GetCertificateAppend CKYISOStatus *apduRC); /*CKYStatus CACApplet_GetProperties(); */ -CKYStatus CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, - CKYISOStatus *apduRC); +CKYStatus CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, + int local, CKYISOStatus *apduRC); +/* Select a PIV applet */ +CKYStatus PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC); + +CKYStatus PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, + int tag, CKYISOStatus *apduRC); +CKYStatus PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, + unsigned int keySize, int derive, + const CKYBuffer *data, CKYBuffer *result, + CKYISOStatus *apduRC); /* * There are 3 read commands: * @@ -553,6 +599,18 @@ CKYStatus CKYApplet_GetIssuerInfo(CKYCar CKYStatus CKYApplet_GetBuiltinACL(CKYCardConnection *conn, CKYAppletRespGetBuiltinACL *gba, CKYISOStatus *apduRC); +/** ECC commands + * * */ + +CKYStatus CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber, + const CKYBuffer *data, CKYBuffer *sig, + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC); + +CKYStatus +CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber, + const CKYBuffer *publicValue, CKYBuffer *sharedSecret, + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC); + /* * deprecates 0.x functions diff -up ./src/libckyapplet/cky_base.c.piv-ecc ./src/libckyapplet/cky_base.c --- ./src/libckyapplet/cky_base.c.piv-ecc 2013-09-08 15:50:33.100428354 -0700 +++ ./src/libckyapplet/cky_base.c 2013-09-08 15:50:33.128428824 -0700 @@ -41,6 +41,7 @@ ckyBuffer_initBuffer(CKYBuffer *buf) buf->data = NULL; buf->size = 0; buf->len = 0; + buf->reserved = NULL; /* make coverity happy */ } /* @@ -573,6 +574,7 @@ CKYAPDU_Init(CKYAPDU *apdu) assert(sizeof(CKYAPDU) == sizeof(CKYAPDUPublic)); #endif ckyBuffer_initBuffer(&apdu->apduBuf); + apdu->reserved = NULL; return CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_MIN_LEN); } @@ -583,6 +585,7 @@ CKYAPDU_InitFromData(CKYAPDU *apdu, cons assert(sizeof(CKYAPDU) == sizeof(CKYAPDUPublic)); #endif ckyBuffer_initBuffer(&apdu->apduBuf); + apdu->reserved = NULL; if (len > CKYAPDU_MAX_DATA_LEN) { return CKYDATATOOLONG; } @@ -710,8 +713,15 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, recvlen); } +CKYStatus +CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen) +{ + return CKYBuffer_AppendChar(&apdu->apduBuf, recvlen); +} + + void -CKY_SetName(char *p) +CKY_SetName(const char *p) { } diff -up ./src/libckyapplet/cky_base.h.piv-ecc ./src/libckyapplet/cky_base.h --- ./src/libckyapplet/cky_base.h.piv-ecc 2013-09-08 15:50:33.100428354 -0700 +++ ./src/libckyapplet/cky_base.h 2013-09-08 15:50:33.128428824 -0700 @@ -278,9 +278,10 @@ CKYStatus CKYAPDU_AppendSendDataBuffer(C /* set Le in the APDU header to the amount of bytes expected to be * returned. */ CKYStatus CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKYByte recvlen); +CKYStatus CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen); /* set the parent loadmodule name */ -void CKY_SetName(char *name); +void CKY_SetName(const char *name); CKY_END_PROTOS diff -up ./src/libckyapplet/cky_card.c.piv-ecc ./src/libckyapplet/cky_card.c --- ./src/libckyapplet/cky_card.c.piv-ecc 2013-09-08 15:50:33.109428505 -0700 +++ ./src/libckyapplet/cky_card.c 2013-09-08 15:50:33.128428824 -0700 @@ -27,6 +27,7 @@ #ifndef WINAPI #define WINAPI +typedef SCARD_READERSTATE *LPSCARD_READERSTATE; #endif #ifndef SCARD_E_NO_READERS_AVAILABLE @@ -843,6 +844,11 @@ CKYCardContext_WaitForStatusChange(CKYCa rv = ctx->scard->SCardGetStatusChange(ctx->context, timeout, readers, readerCount); if (rv != SCARD_S_SUCCESS) { + if ((rv == SCARD_E_NO_SERVICE) || (rv == SCARD_E_SERVICE_STOPPED)) { + /* if we were stopped, don't reuse the old context, + * pcsc-lite hangs */ + ckyCardContext_release(ctx); + } ctx->lastError = rv; return CKYSCARDERR; } @@ -1071,25 +1077,39 @@ CKYCardConnection_ExchangeAPDU(CKYCardCo CKYBuffer *response) { CKYStatus ret; + CKYBuffer getResponse; + CKYSize size = 0; ret = CKYCardConnection_TransmitAPDU(conn, apdu, response); if (ret != CKYSUCCESS) { return ret; } + CKYBuffer_InitEmpty(&getResponse); - if (CKYBuffer_Size(response) == 2 && CKYBuffer_GetChar(response,0) == 0x61) { + /* automatically handle the response data protocol */ + while ((ret == CKYSUCCESS) && + (size = CKYBuffer_Size(response)) >= 2 && + (CKYBuffer_GetChar(response,size-2) == 0x61)) { /* get the response */ CKYAPDU getResponseAPDU; + CKYBuffer_Zero(&getResponse); CKYAPDU_Init(&getResponseAPDU); CKYAPDU_SetCLA(&getResponseAPDU, 0x00); CKYAPDU_SetINS(&getResponseAPDU, 0xc0); CKYAPDU_SetP1(&getResponseAPDU, 0x00); CKYAPDU_SetP2(&getResponseAPDU, 0x00); - CKYAPDU_SetReceiveLen(&getResponseAPDU, CKYBuffer_GetChar(response,1)); - ret = CKYCardConnection_TransmitAPDU(conn, &getResponseAPDU, response); + CKYAPDU_SetReceiveLen(&getResponseAPDU, + CKYBuffer_GetChar(response,size-1)); + ret = CKYCardConnection_TransmitAPDU(conn, &getResponseAPDU, + &getResponse); CKYAPDU_FreeData(&getResponseAPDU); + if ((ret == CKYSUCCESS) && (CKYBuffer_Size(&getResponse) >= 2)) { + CKYBuffer_Resize(response, size-2); + CKYBuffer_AppendCopy(response,&getResponse); + } } + CKYBuffer_FreeData(&getResponse); return ret; } diff -up ./src/libckyapplet/cky_card.h.piv-ecc ./src/libckyapplet/cky_card.h diff -up ./src/libckyapplet/cky_factory.c.piv-ecc ./src/libckyapplet/cky_factory.c --- ./src/libckyapplet/cky_factory.c.piv-ecc 2013-09-08 15:50:33.101428370 -0700 +++ ./src/libckyapplet/cky_factory.c 2013-09-08 15:50:33.129428841 -0700 @@ -62,7 +62,6 @@ CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu CKYAPDU_SetP2(apdu, 0x7f); return CKYAPDU_SetReceiveLen(apdu, CKY_SIZE_GET_CPLCDATA); } - /* * applet commands must be issued with the appplet selected. */ @@ -184,6 +183,49 @@ fail: } CKYStatus +CKYAPDUFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, CKYByte keyNumber, + CKYByte location, + const CKYBuffer *publicData, const CKYBuffer *secretKey) +{ + CKYStatus ret = CKYINVALIDARGS; + CKYSize len; + CKYBuffer buf; + + if (!publicData) + return ret; + + if (!(len = CKYBuffer_Size(publicData))) + return ret; + + CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); + CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_ECC_KEY_AGREEMENT); + CKYAPDU_SetP1(apdu, keyNumber); + CKYAPDU_SetP2(apdu, CKY_CIPHER_ONE_STEP); + + CKYBuffer_InitEmpty(&buf); + + ret = CKYBuffer_Reserve(&buf, 3); + + if (ret == CKYSUCCESS) + ret = CKYBuffer_AppendChar(&buf, location); + if (ret == CKYSUCCESS) + ret = CKYBuffer_AppendShort(&buf, (unsigned short)len); + if (ret == CKYSUCCESS) + ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); + if (ret == CKYSUCCESS) + ret = CKYAPDU_AppendSendDataBuffer(apdu, publicData); + if (ret == CKYSUCCESS && secretKey && 0 < (len = CKYBuffer_Size(secretKey))) { + CKYBuffer_Resize(&buf,2); + CKYBuffer_SetShort(&buf, 0, (unsigned short)len); + ret = CKYAPDU_AppendSendDataBuffer(apdu, &buf); + if (ret == CKYSUCCESS) + ret = CKYAPDU_AppendSendDataBuffer(apdu, secretKey); + } + CKYBuffer_FreeData(&buf); + return ret; +} + +CKYStatus CKYAPDUFactory_ComputeCryptOneStep(CKYAPDU *apdu, CKYByte keyNumber, CKYByte mode, CKYByte direction, CKYByte location, const CKYBuffer *idata, const CKYBuffer *sig) @@ -386,6 +428,49 @@ fail: } CKYStatus +CKYAPDUFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, CKYByte keyNumber, + CKYByte location, + const CKYBuffer *idata, const CKYBuffer *sig) +{ + CKYStatus ret = CKYINVALIDARGS; + CKYSize len; + CKYBuffer buf; + + if (!idata) + return ret; + + if (!(len = CKYBuffer_Size(idata)) && location != CKY_DL_OBJECT) + return ret; + + CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); + CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_ECC_SIGNATURE); + CKYAPDU_SetP1(apdu, keyNumber); + CKYAPDU_SetP2(apdu, CKY_CIPHER_ONE_STEP); + + CKYBuffer_InitEmpty(&buf); + + ret = CKYBuffer_Reserve(&buf, 3); + + if (ret == CKYSUCCESS) + ret = CKYBuffer_AppendChar(&buf, location); + if (ret == CKYSUCCESS) + ret = CKYBuffer_AppendShort(&buf, (unsigned short)len); + if (ret == CKYSUCCESS) + ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); + if (ret == CKYSUCCESS) + ret = CKYAPDU_AppendSendDataBuffer(apdu, idata); + if (ret == CKYSUCCESS && sig && 0 < (len = CKYBuffer_Size(sig))) { + CKYBuffer_Resize(&buf,2); + CKYBuffer_SetShort(&buf, 0, (unsigned short)len); + ret = CKYAPDU_AppendSendDataBuffer(apdu, &buf); + if (ret == CKYSUCCESS) + ret = CKYAPDU_AppendSendDataBuffer(apdu, sig); + } + CKYBuffer_FreeData(&buf); + return ret; +} + +CKYStatus CKYAPDUFactory_ReadObject(CKYAPDU *apdu, unsigned long objectID, CKYOffset offset, CKYByte size) { @@ -622,7 +707,6 @@ fail: CKYBuffer_FreeData(&buf); return ret; } - CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu) { @@ -634,7 +718,7 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap } CKYStatus -CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin) +CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const char *pin) { CKYStatus ret; CKYSize size; @@ -642,7 +726,7 @@ CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN); CKYAPDU_SetP1(apdu, 0x00); - CKYAPDU_SetP2(apdu, 0x00); + CKYAPDU_SetP2(apdu, keyRef); /* no pin, send an empty buffer */ if (!pin) { return CKYAPDU_SetReceiveLen(apdu, 0); @@ -663,3 +747,63 @@ CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, return ret; } + +CKYStatus +PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, + CKYByte key, int len, const CKYBuffer *data) +{ + CKYStatus ret; + CKYAPDU_SetCLA(apdu, chain ? CKY_CLASS_ISO7816_CHAIN : + CKY_CLASS_ISO7816); + CKYAPDU_SetINS(apdu, PIV_INS_GEN_AUTHENTICATE); + CKYAPDU_SetP1(apdu, alg); + CKYAPDU_SetP2(apdu, key); + ret = CKYAPDU_SetSendDataBuffer(apdu, data); + if (ret == CKYSUCCESS && chain == 0 && len != 0) { + if (len >= 256) len = 0; + ret = CKYAPDU_AppendReceiveLen(apdu, len); + } + return ret; +} + +CKYStatus +PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, CKYByte count) +{ + CKYStatus ret; + CKYBuffer buf; + CKYByte objectSize; + + CKYBuffer_InitEmpty(&buf); + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); + CKYAPDU_SetINS(apdu, 0xcb); + CKYAPDU_SetP1(apdu, 0x3f); + CKYAPDU_SetP2(apdu, 0xff); + + objectSize = CKYBuffer_Size(object); + + ret = CKYBuffer_Reserve(&buf, 2+objectSize); + if (ret != CKYSUCCESS) { + goto fail; + } + ret = CKYBuffer_AppendChar(&buf, 0x5c); + if (ret != CKYSUCCESS) { + goto fail; + } + ret = CKYBuffer_AppendChar(&buf, objectSize); + if (ret != CKYSUCCESS) { + goto fail; + } + ret = CKYBuffer_AppendCopy(&buf, object); + if (ret != CKYSUCCESS) { + goto fail; + } + ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); + if (ret != CKYSUCCESS) { + goto fail; + } + ret = CKYAPDU_AppendReceiveLen(apdu, count); +fail: + CKYBuffer_FreeData(&buf); + return ret; +} + diff -up ./src/libckyapplet/cky_factory.h.piv-ecc ./src/libckyapplet/cky_factory.h --- ./src/libckyapplet/cky_factory.h.piv-ecc 2013-09-08 15:50:33.101428370 -0700 +++ ./src/libckyapplet/cky_factory.h 2013-09-08 15:50:33.130428858 -0700 @@ -25,10 +25,11 @@ /* * Various Class bytes */ -#define CKY_CLASS_ISO7816 0x00 +#define CKY_CLASS_ISO7816 0x00 +#define CKY_CLASS_ISO7816_CHAIN 0x10 #define CKY_CLASS_GLOBAL_PLATFORM 0x80 -#define CKY_CLASS_SECURE 0x84 -#define CKY_CLASS_COOLKEY 0xb0 +#define CKY_CLASS_SECURE 0x84 +#define CKY_CLASS_COOLKEY 0xb0 /* * Applet Instruction Bytes @@ -66,6 +67,8 @@ /* nonce validated & Secure Channel */ #define CKY_INS_IMPORT_KEY 0x32 #define CKY_INS_COMPUTE_CRYPT 0x36 +#define CKY_INS_COMPUTE_ECC_SIGNATURE 0x37 +#define CKY_INS_COMPUTE_ECC_KEY_AGREEMENT 0x38 #define CKY_INS_CREATE_PIN 0x40 #define CKY_INS_CHANGE_PIN 0x44 #define CKY_INS_CREATE_OBJ 0x5A @@ -91,6 +94,12 @@ #define CAC_SIZE_GET_PROPERTIES 48 #define CAC_P1_STEP 0x80 #define CAC_P1_FINAL 0x00 +#define CAC_LOGIN_GLOBAL 0x00 + +/* PIV */ +#define PIV_LOGIN_LOCAL 0x80 +#define PIV_LOGIN_GLOBAL CAC_LOGIN_GLOBAL +#define PIV_INS_GEN_AUTHENTICATE 0x87 /* * Fixed return sized from various commands @@ -123,6 +132,7 @@ #define CKY_DES_ECB_NOPAD 0x21 /* operations (Cipher Direction) */ +#define CKY_DIR_NONE 0x00 #define CKY_DIR_SIGN 0x01 #define CKY_DIR_VERIFY 0x02 #define CKY_DIR_ENCRYPT 0x03 @@ -187,6 +197,12 @@ CKYStatus CKYAPDUFactory_ComputeCryptFin CKYStatus CKYAPDUFactory_ComputeCryptOneStep(CKYAPDU *apdu, CKYByte keyNumber, CKYByte mode, CKYByte direction, CKYByte location, const CKYBuffer *data, const CKYBuffer *sig); +CKYStatus CKYAPDUFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, CKYByte keyNumber, + CKYByte location, + const CKYBuffer *data, const CKYBuffer *sig); +CKYStatus CKYAPDUFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, CKYByte keyNumber, + CKYByte location, + const CKYBuffer *publicData, const CKYBuffer *secretKey); CKYStatus CKYAPDUFactory_CreatePIN(CKYAPDU *apdu, CKYByte pinNumber, CKYByte maxAttempts, const char *pinValue); CKYStatus CKYAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte pinNumber, @@ -218,11 +234,16 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, const CKYBuffer *data); -CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin); +CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, + const char *pin); CKYStatus CACAPDUFactory_GetCertificate(CKYAPDU *apdu, CKYSize size); CKYStatus CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, CKYByte type, CKYByte count); CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu); +CKYStatus PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, + CKYByte count); +CKYStatus PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, + CKYByte key, int len, const CKYBuffer *data); CKY_END_PROTOS