Index: build/chrome/cackey-chrome-test.c ================================================================== --- build/chrome/cackey-chrome-test.c +++ build/chrome/cackey-chrome-test.c @@ -1,14 +1,53 @@ +#include +#include +#include #include #include "cackey-chrome.h" int main(int argc, char **argv) { struct cackey_certificate *certificates; - int numCertificates; + char *pinPrompt = NULL, pin[32]; + unsigned char signBuffer[1024]; + int numCertificates, idxCertificate; + unsigned long signLength; + cackey_chrome_returnType rvSign; numCertificates = cackey_chrome_listCertificates(&certificates); printf("numCertificates = %i\n", numCertificates); + + for (idxCertificate = 0; idxCertificate < numCertificates; idxCertificate++) { + printf("Certificate #%i: %lu bytes\n", idxCertificate, certificates[idxCertificate].certificate_len); + + signLength = sizeof(signBuffer); + rvSign = cackey_chrome_signMessage(&certificates[idxCertificate], "Test", 4, signBuffer, &signLength, &pinPrompt, NULL); + + if (rvSign == CACKEY_CHROME_NEEDLOGIN) { + if (pinPrompt == NULL) { + pinPrompt = strdup("Please enter your PIN: "); + } + + printf("%s: ", pinPrompt); + fflush(stdout); + + free(pinPrompt); + + fgets(pin, sizeof(pin), stdin); + while (strlen(pin) >= 1 && pin[strlen(pin) - 1] == '\n') { + pin[strlen(pin) - 1] = '\0'; + } + + signLength = sizeof(signBuffer); + rvSign = cackey_chrome_signMessage(&certificates[idxCertificate], "Test", 4, signBuffer, &signLength, &pinPrompt, pin); + } + + printf("Signed message \"Test\": %lu bytes (return value = %i)\n", signLength, rvSign); + } + + cackey_chrome_freeCertificates(certificates, numCertificates); + + cackey_chrome_terminate(); return(0); } Index: build/chrome/cackey-chrome.c ================================================================== --- build/chrome/cackey-chrome.c +++ build/chrome/cackey-chrome.c @@ -2,17 +2,20 @@ extern "C" { #endif #include #include +#include +#include #include "mypkcs11.h" #include "cackey-chrome.h" struct cackey_chrome_id { - unsigned char *id; + void *id; size_t idLen; + int initialized; }; static CK_FUNCTION_LIST_PTR moduleFunctionList = NULL; static CK_RV cackey_chrome_init(void) { @@ -40,10 +43,69 @@ return(chk_rv); } return(CKR_OK); } + +void cackey_chrome_terminate(void) { + if (!moduleFunctionList) { + return; + } + + moduleFunctionList->C_Finalize(NULL); + + free(moduleFunctionList); + + moduleFunctionList = NULL; + + return; +} + +static CK_RV cackey_chrome_GetAttributesFromTemplate(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE *attrTemplate, CK_ULONG attrTemplateCount) { + CK_RV chk_rv; + CK_ATTRIBUTE *currAttr; + CK_ULONG currAttrIndex; + + for (currAttrIndex = 0; currAttrIndex < attrTemplateCount; currAttrIndex++) { + currAttr = &attrTemplate[currAttrIndex]; + + currAttr->pValue = NULL; + currAttr->ulValueLen = 0; + } + + chk_rv = moduleFunctionList->C_GetAttributeValue(hSession, hObject, attrTemplate, attrTemplateCount); + if (chk_rv == CKR_ATTRIBUTE_TYPE_INVALID || chk_rv == CKR_ATTRIBUTE_SENSITIVE || chk_rv == CKR_BUFFER_TOO_SMALL) { + chk_rv = CKR_OK; + } + + if (chk_rv != CKR_OK) { + return(chk_rv); + } + + for (currAttrIndex = 0; currAttrIndex < attrTemplateCount; currAttrIndex++) { + currAttr = &attrTemplate[currAttrIndex]; + + if (currAttr->ulValueLen == 0) { + continue; + } + + if (((CK_LONG) currAttr->ulValueLen) == ((CK_LONG) -1)) { + continue; + } + + currAttr->pValue = malloc(currAttr->ulValueLen); + } + + chk_rv = moduleFunctionList->C_GetAttributeValue(hSession, hObject, attrTemplate, attrTemplateCount); + if (chk_rv != CKR_OK) { + free(currAttr->pValue); + + return(chk_rv); + } + + return(CKR_OK); +} int cackey_chrome_listCertificates(struct cackey_certificate **certificates) { CK_RV chk_rv; CK_ULONG numSlots, currSlot; CK_SLOT_ID_PTR slots; @@ -56,15 +118,23 @@ }; CK_ATTRIBUTE searchTemplateCertificates[] = { {CKA_CLASS, NULL, sizeof(CK_OBJECT_CLASS)}, {CKA_ID, NULL, 0} }; - CK_ATTRIBUTE attrTemplate[] = { + CK_ATTRIBUTE attrTemplatePrivateKey[] = { {CKA_ID, NULL, 0} - }, *currAttr; - CK_ULONG currAttrIndex; + }; + CK_ATTRIBUTE attrTemplateCertificate[] = { + {CKA_VALUE, NULL, 0} + }; CK_OBJECT_CLASS objectClassPrivateKey = CKO_PRIVATE_KEY; + CK_OBJECT_CLASS objectClassCertificate = CKO_CERTIFICATE; + struct cackey_chrome_id *ids; + int idsCount, currId; + int foundCertificates, certificatesCount; + + *certificates = NULL; chk_rv = cackey_chrome_init(); if (chk_rv != CKR_OK) { return(0); } @@ -76,14 +146,28 @@ slots = malloc(sizeof(*slots) * numSlots); chk_rv = moduleFunctionList->C_GetSlotList(FALSE, slots, &numSlots); if (chk_rv != CKR_OK) { + free(slots); + return(0); } searchTemplatePrivateKeys[0].pValue = &objectClassPrivateKey; + searchTemplateCertificates[0].pValue = &objectClassCertificate; + + foundCertificates = 0; + certificatesCount = 10; + *certificates = malloc(sizeof(**certificates) * certificatesCount); + + idsCount = 10; + ids = malloc(sizeof(*ids) * idsCount); + + for (currId = 0; currId < idsCount; currId++) { + ids[currId].initialized = 0; + } for (currSlot = 0; currSlot < numSlots; currSlot++) { chk_rv = moduleFunctionList->C_GetSlotInfo(slots[currSlot], &slotInfo); if (chk_rv != CKR_OK) { continue; @@ -102,10 +186,234 @@ if (chk_rv != CKR_OK) { moduleFunctionList->C_CloseSession(hSession); continue; } + + for (currId = 0; currId < idsCount; currId++) { + if (!ids[currId].initialized) { + continue; + } + + free(ids[currId].id); + + ids[currId].initialized = 0; + } + + currId = 0; + + while (1) { + chk_rv = moduleFunctionList->C_FindObjects(hSession, &hObject, 1, &ulObjectCount); + if (chk_rv != CKR_OK) { + break; + } + + if (ulObjectCount == 0) { + break; + } + + if (ulObjectCount != 1) { + break; + } + + chk_rv = cackey_chrome_GetAttributesFromTemplate(hSession, hObject, attrTemplatePrivateKey, sizeof(attrTemplatePrivateKey) / sizeof(attrTemplatePrivateKey[0])); + if (chk_rv != CKR_OK) { + continue; + } + + if (currId >= idsCount) { + idsCount *= 2; + + ids = realloc(ids, sizeof(*ids) * idsCount); + } + + ids[currId].idLen = attrTemplatePrivateKey[0].ulValueLen; + ids[currId].id = attrTemplatePrivateKey[0].pValue; + ids[currId].initialized = 1; + currId++; + } + + moduleFunctionList->C_FindObjectsFinal(hSession); + + for (currId = 0; currId < idsCount; currId++) { + if (!ids[currId].initialized) { + continue; + } + + searchTemplateCertificates[1].pValue = ids[currId].id; + searchTemplateCertificates[1].ulValueLen = ids[currId].idLen; + + chk_rv = moduleFunctionList->C_FindObjectsInit(hSession, searchTemplateCertificates, sizeof(searchTemplateCertificates) / sizeof(searchTemplateCertificates[0])); + if (chk_rv != CKR_OK) { + free(ids[currId].id); + + ids[currId].initialized = 0; + + continue; + } + + while (1) { + chk_rv = moduleFunctionList->C_FindObjects(hSession, &hObject, 1, &ulObjectCount); + if (chk_rv != CKR_OK) { + break; + } + + if (ulObjectCount == 0) { + break; + } + + if (ulObjectCount != 1) { + break; + } + + chk_rv = cackey_chrome_GetAttributesFromTemplate(hSession, hObject, attrTemplateCertificate, sizeof(attrTemplateCertificate) / sizeof(attrTemplateCertificate[0])); + if (chk_rv != CKR_OK) { + continue; + } + + if (foundCertificates >= certificatesCount) { + certificatesCount *= 2; + *certificates = realloc(*certificates, sizeof(**certificates) * certificatesCount); + } + + (*certificates)[foundCertificates].certificate = malloc(attrTemplateCertificate[0].ulValueLen); + memcpy((*certificates)[foundCertificates].certificate, attrTemplateCertificate[0].pValue, attrTemplateCertificate[0].ulValueLen); + (*certificates)[foundCertificates].certificate_len = attrTemplateCertificate[0].ulValueLen; + + free(attrTemplateCertificate[0].pValue); + + foundCertificates++; + } + + moduleFunctionList->C_FindObjectsFinal(hSession); + + free(ids[currId].id); + + ids[currId].initialized = 0; + } + + moduleFunctionList->C_CloseSession(hSession); + } + + for (currId = 0; currId < idsCount; currId++) { + if (!ids[currId].initialized) { + continue; + } + + free(ids[currId].id); + + ids[currId].initialized = 0; + } + + free(ids); + + free(slots); + + return(foundCertificates); +} + +void cackey_chrome_freeCertificates(struct cackey_certificate *certificates, int certificatesCount) { + int idx; + + if (certificates == NULL) { + return; + } + + for (idx = 0; idx < certificatesCount; idx++) { + if (certificates[idx].certificate) { + free(certificates[idx].certificate); + } + } + + free(certificates); + + return; +} + +cackey_chrome_returnType cackey_chrome_signMessage(struct cackey_certificate *certificate, void *data, unsigned long dataLength, unsigned char *destination, unsigned long *destinationLength, char **pinPrompt, char *pin) { + CK_RV chk_rv; + CK_ULONG numSlots, currSlot; + CK_SLOT_ID_PTR slots; + CK_SLOT_INFO slotInfo; + CK_SESSION_HANDLE hSession; + CK_OBJECT_HANDLE hObject, hKey; + CK_ULONG ulObjectCount; + CK_ATTRIBUTE searchTemplateCertificates[] = { + {CKA_CLASS, NULL, sizeof(CK_OBJECT_CLASS)}, + {CKA_VALUE, NULL, 0} + }; + CK_ATTRIBUTE searchTemplatePrivateKeys[] = { + {CKA_CLASS, NULL, sizeof(CK_OBJECT_CLASS)}, + {CKA_ID, NULL, 0} + }; + CK_ATTRIBUTE attrTemplateCertificate[] = { + {CKA_ID, NULL, 0}, + {CKA_LABEL, NULL, 0} + }; + CK_MECHANISM signMechanism = {CKM_RSA_PKCS, NULL, 0}; + CK_OBJECT_CLASS objectClassPrivateKey = CKO_PRIVATE_KEY; + CK_OBJECT_CLASS objectClassCertificate = CKO_CERTIFICATE; + CK_TOKEN_INFO tokenInfo; + CK_ULONG tmpDestinationLength; + char *certificateLabel; + int foundPrivateKeyObject; + cackey_chrome_returnType retval; + + *pinPrompt = NULL; + + retval = CACKEY_CHROME_ERROR; + + chk_rv = cackey_chrome_init(); + if (chk_rv != CKR_OK) { + return(retval); + } + + chk_rv = moduleFunctionList->C_GetSlotList(FALSE, NULL, &numSlots); + if (chk_rv != CKR_OK) { + return(retval); + } + + slots = malloc(sizeof(*slots) * numSlots); + + chk_rv = moduleFunctionList->C_GetSlotList(FALSE, slots, &numSlots); + if (chk_rv != CKR_OK) { + free(slots); + + return(retval); + } + + searchTemplateCertificates[0].pValue = &objectClassCertificate; + searchTemplatePrivateKeys[0].pValue = &objectClassPrivateKey; + + searchTemplateCertificates[1].pValue = certificate->certificate; + searchTemplateCertificates[1].ulValueLen = certificate->certificate_len; + + foundPrivateKeyObject = 0; + + certificateLabel = NULL; + + for (currSlot = 0; currSlot < numSlots; currSlot++) { + chk_rv = moduleFunctionList->C_GetSlotInfo(slots[currSlot], &slotInfo); + if (chk_rv != CKR_OK) { + continue; + } + + if ((slotInfo.flags & CKF_TOKEN_PRESENT) != CKF_TOKEN_PRESENT) { + continue; + } + + chk_rv = moduleFunctionList->C_OpenSession(slots[currSlot], CKF_SERIAL_SESSION, NULL, NULL, &hSession); + if (chk_rv != CKR_OK) { + continue; + } + + chk_rv = moduleFunctionList->C_FindObjectsInit(hSession, searchTemplateCertificates, sizeof(searchTemplateCertificates) / sizeof(searchTemplateCertificates[0])); + if (chk_rv != CKR_OK) { + moduleFunctionList->C_CloseSession(hSession); + + continue; + } while (1) { chk_rv = moduleFunctionList->C_FindObjects(hSession, &hObject, 1, &ulObjectCount); if (chk_rv != CKR_OK) { break; @@ -117,53 +425,152 @@ if (ulObjectCount != 1) { break; } - for (currAttrIndex = 0; currAttrIndex < (sizeof(attrTemplate) / sizeof(attrTemplate[0])); currAttrIndex++) { - currAttr = &attrTemplate[currAttrIndex]; - - currAttr->pValue = NULL; - currAttr->ulValueLen = 0; - } - - chk_rv = C_GetAttributeValue(hSession, hObject, attrTemplate, sizeof(attrTemplate) / sizeof(attrTemplate[0])); - if (chk_rv == CKR_ATTRIBUTE_TYPE_INVALID || chk_rv == CKR_ATTRIBUTE_SENSITIVE || chk_rv == CKR_BUFFER_TOO_SMALL) { - chk_rv = CKR_OK; - } - - if (chk_rv != CKR_OK) { - continue; - } - - for (currAttrIndex = 0; currAttrIndex < (sizeof(attrTemplate) / sizeof(attrTemplate[0])); currAttrIndex++) { - currAttr = &attrTemplate[currAttrIndex]; - - if (currAttr->ulValueLen == 0) { - continue; - } - - if (((CK_LONG) currAttr->ulValueLen) == ((CK_LONG) -1)) { - continue; - } - - currAttr->pValue = malloc(currAttr->ulValueLen); - } - - chk_rv = C_GetAttributeValue(hSession, hObject, attrTemplate, sizeof(attrTemplate) / sizeof(attrTemplate[0])); - if (chk_rv != CKR_OK) { - continue; - } - - } - - moduleFunctionList->C_FindObjectsFinal(hSession); - - moduleFunctionList->C_CloseSession(hSession); - } - - return(0); + chk_rv = cackey_chrome_GetAttributesFromTemplate(hSession, hObject, attrTemplateCertificate, sizeof(attrTemplateCertificate) / sizeof(attrTemplateCertificate[0])); + if (chk_rv != CKR_OK) { + continue; + } + + searchTemplatePrivateKeys[1].pValue = attrTemplateCertificate[0].pValue; + searchTemplatePrivateKeys[1].ulValueLen = attrTemplateCertificate[0].ulValueLen; + + if (attrTemplateCertificate[1].ulValueLen > 0 && attrTemplateCertificate[1].pValue != NULL) { + certificateLabel = malloc(attrTemplateCertificate[1].ulValueLen + 1); + memcpy(certificateLabel, attrTemplateCertificate[1].pValue, attrTemplateCertificate[1].ulValueLen); + certificateLabel[attrTemplateCertificate[1].ulValueLen] = '\0'; + } + + break; + } + + moduleFunctionList->C_FindObjectsFinal(hSession); + + if (searchTemplatePrivateKeys[1].pValue != NULL) { + chk_rv = moduleFunctionList->C_FindObjectsInit(hSession, searchTemplateCertificates, sizeof(searchTemplateCertificates) / sizeof(searchTemplateCertificates[0])); + if (chk_rv == CKR_OK) { + while (1) { + chk_rv = moduleFunctionList->C_FindObjects(hSession, &hObject, 1, &ulObjectCount); + if (chk_rv != CKR_OK) { + break; + } + + if (ulObjectCount == 0) { + break; + } + + if (ulObjectCount != 1) { + break; + } + + hKey = hObject; + + foundPrivateKeyObject = 1; + + break; + } + + moduleFunctionList->C_FindObjectsFinal(hSession); + } + + free(searchTemplatePrivateKeys[1].pValue); + + } + + if (foundPrivateKeyObject) { + chk_rv = moduleFunctionList->C_SignInit(hSession, &signMechanism, hKey); + if (chk_rv != CKR_OK) { + break; + } + + tmpDestinationLength = *destinationLength; + chk_rv = moduleFunctionList->C_Sign(hSession, data, dataLength, destination, &tmpDestinationLength); + switch (chk_rv) { + case CKR_OK: + *destinationLength = tmpDestinationLength; + retval = CACKEY_CHROME_OK; + break; + case CKR_USER_NOT_LOGGED_IN: + chk_rv = moduleFunctionList->C_GetTokenInfo(slots[currSlot], &tokenInfo); + if (chk_rv == CKR_OK) { + if ((tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == CKF_PROTECTED_AUTHENTICATION_PATH) { + retval = CACKEY_CHROME_NEEDPROTECTEDLOGIN; + } else { + retval = CACKEY_CHROME_NEEDLOGIN; + + *pinPrompt = malloc(1024); + if (certificateLabel) { + snprintf(*pinPrompt, 1024, "Please enter the PIN for %s:%s", tokenInfo.label, certificateLabel); + } else { + snprintf(*pinPrompt, 1024, "Please enter the PIN for %s", tokenInfo.label); + } + } + } else { + retval = CACKEY_CHROME_NEEDLOGIN; + + *pinPrompt = strdup("Please enter your Smartcard PIN"); + } + + if (retval == CACKEY_CHROME_NEEDPROTECTEDLOGIN) { + retval = CACKEY_CHROME_ERROR; + + chk_rv = moduleFunctionList->C_Login(hSession, CKU_USER, NULL, 0); + } else { + if (pin) { + retval = CACKEY_CHROME_ERROR; + + free(*pinPrompt); + *pinPrompt = NULL; + + chk_rv = moduleFunctionList->C_Login(hSession, CKU_USER, (CK_UTF8CHAR_PTR) pin, strlen(pin)); + } else { + chk_rv = CKR_GENERAL_ERROR; + } + } + + if (chk_rv == CKR_OK && retval == CACKEY_CHROME_ERROR) { + chk_rv = moduleFunctionList->C_SignInit(hSession, &signMechanism, hKey); + if (chk_rv != CKR_OK) { + break; + } + + tmpDestinationLength = *destinationLength; + chk_rv = moduleFunctionList->C_Sign(hSession, data, dataLength, destination, &tmpDestinationLength); + switch (chk_rv) { + case CKR_OK: + *destinationLength = tmpDestinationLength; + retval = CACKEY_CHROME_OK; + break; + case CKR_USER_NOT_LOGGED_IN: + retval = CACKEY_CHROME_NEEDLOGIN; + break; + default: + retval = CACKEY_CHROME_ERROR; + break; + } + } + + break; + default: + retval = CACKEY_CHROME_ERROR; + break; + } + + break; + } + + moduleFunctionList->C_CloseSession(hSession); + } + + free(slots); + + if (certificateLabel) { + free(certificateLabel); + } + + return(retval); } #ifdef __cplusplus } #endif Index: build/chrome/cackey-chrome.h ================================================================== --- build/chrome/cackey-chrome.h +++ build/chrome/cackey-chrome.h @@ -10,12 +10,24 @@ struct cackey_certificate { size_t certificate_len; unsigned char *certificate; }; +typedef enum { + CACKEY_CHROME_OK, + CACKEY_CHROME_ERROR, + CACKEY_CHROME_NEEDLOGIN, + CACKEY_CHROME_NEEDPROTECTEDLOGIN +} cackey_chrome_returnType; + int cackey_chrome_listCertificates(struct cackey_certificate **certificates); +void cackey_chrome_freeCertificates(struct cackey_certificate *certificates, int certificatesCount); + +cackey_chrome_returnType cackey_chrome_signMessage(struct cackey_certificate *certificate, void *data, unsigned long dataLength, unsigned char *destination, unsigned long *destinationLength, char **pinPrompt, char *pin); + +void cackey_chrome_terminate(void); # ifdef __cplusplus } # endif #endif