Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -22,10 +22,13 @@ test-afl test-afl.data build/chrome/archive build/chrome/workdir-* build/chrome/lib +build/chrome/include build/chrome/cackey-chrome.o build/chrome/cackey-chrome-init.o build/chrome/cackey.pexe build/chrome/cackey.nmf build/chrome/cackey.crx +build/chrome/test +build/chrome/google-pcsc.js Index: build/chrome/Makefile ================================================================== --- build/chrome/Makefile +++ build/chrome/Makefile @@ -5,25 +5,31 @@ FINALIZE = pnacl-finalize CACKEY_LIBS = -Llib -lcackey -lz PCSC_LIBS = -Llib -lpcsc -L${NACL_SDK_ROOT}/lib/pnacl/Release -lppapi -lppapi_cpp LIBS = $(CACKEY_LIBS) $(PCSC_LIBS) -CFLAGS = -Wall -I${NACL_SDK_ROOT}/include -CXXFLAGS = $(CFLAGS) +CFLAGS = -Wall -g3 -ggdb3 -I${NACL_SDK_ROOT}/include -I../../pkcs11 -Iinclude/PCSC +CXXFLAGS = $(CFLAGS) -std=gnu++11 +LDFLAGS = -g3 -ggdb3 PATH += :${NACL_SDK_ROOT}/toolchain/linux_pnacl/bin export PATH +ifeq (,${NACL_SDK_ROOT}) +$(error "Please set NACL_SDK_ROOT") +endif +export NACL_SDK_ROOT + all: cackey.crx -cackey.crx: cackey.pexe cackey.nmf manifest.json cackey.js +cackey.crx: cackey.pexe cackey.nmf manifest.json cackey.js google-pcsc.js rm -f cackey.crx zip cackey.crx.new $^ mv cackey.crx.new cackey.crx cackey.pexe: cackey-chrome.o cackey-chrome-init.o lib/libcackey.a lib/libpcsc.a lib/libz.a - $(CXX) -o cackey.pexe.new cackey-chrome.o cackey-chrome-init.o $(LIBS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o cackey.pexe.new cackey-chrome.o cackey-chrome-init.o $(LIBS) $(FINALIZE) cackey.pexe.new mv cackey.pexe.new cackey.pexe cackey.nmf: cackey.pexe ${NACL_SDK_ROOT}/tools/create_nmf.py cackey.pexe > cackey.nmf.new @@ -31,12 +37,12 @@ lib/libcackey.a: build-deps mkdir -p lib rm -f lib/libcackey.a rm -rf workdir-* - ./build-deps - cd lib && ln -s ../workdir-*.inst/lib/libcackey.a . + +./build-deps + cd lib && ln -s ../workdir-*.inst/lib/libcackey_g.a libcackey.a touch lib/libcackey.a lib/libpcsc.a: lib/libcackey.a mkdir -p lib rm -f lib/libpcsc.a @@ -47,18 +53,38 @@ mkdir -p lib rm -f lib/libz.a cd lib && ln -s ../workdir-*.inst/lib/libz.a . touch lib/libz.a -cackey-chrome.o: cackey-chrome.c -cackey-chrome-init.o: cackey-chrome-init.cc +google-pcsc.js: lib/libcackey.a + rm -f google-pcsc.js google-pcsc.js.new + cat workdir-*.inst/js/{scope,logging,pcsc,pcsc-nacl}.js > google-pcsc.js.new + mv google-pcsc.js.new google-pcsc.js + +include/PCSC/pcsc-nacl.h: lib/libcackey.a + mkdir -p include/PCSC + rm -f include/PCSC/pcsc-nacl.h.new include/PCSC/pcsc-nacl.h + cd include/PCSC && ln -s ../../workdir-*.inst/include/PCSC/pcsc-nacl.h pcsc-nacl.h.new + touch include/PCSC/pcsc-nacl.h.new + mv include/PCSC/pcsc-nacl.h.new include/PCSC/pcsc-nacl.h + +test: cackey-chrome.c cackey-chrome-test.c ../../cackey.c Makefile + gcc -g3 -ggdb3 -Wall -I. -I../../pkcs11 -I/opt/appfs/core.appfs.rkeene.org/zlib/platform/latest/include -I/opt/appfs/rkeene.org/pcsc-lite/platform/latest/include/PCSC -DHAVE_WINTYPES_H=1 -DHAVE_PCSCLITE_H=1 -DHAVE_WINSCARD_H=1 -DHAVE_STDINT_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDLIB_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STRING_H=1 -DHAVE_PTHREAD_H=1 -DHAVE_LIMITS_H=1 -DHAVE_STDIO_H=1 -DHAVE_ZLIB_H -DHAVE_LIBZ -DCACKEY_DEBUG=1 -o test cackey-chrome.c cackey-chrome-test.c ../../cackey.c -L/opt/appfs/core.appfs.rkeene.org/zlib/platform/latest/lib -lz -L/opt/appfs/rkeene.org/pcsc-lite/platform/latest/lib -lpcsclite -L/opt/appfs/core.appfs.rkeene.org/glibc/platform/latest/lib -lc -lpthread -Wl,-R,/opt/appfs/core.appfs.rkeene.org/zlib/platform/latest/lib -Wl,-R,/opt/appfs/rkeene.org/pcsc-lite/platform/latest/lib -Wl,-R,/opt/appfs/core.appfs.rkeene.org/glibc/platform/latest/lib -Wl,-dynamic-linker,/opt/appfs/core.appfs.rkeene.org/glibc/platform/latest/lib/ld-linux-x86-64.so.2 + +cackey-chrome.o: cackey-chrome.c cackey-chrome.h +cackey-chrome-init.o: cackey-chrome-init.cc cackey-chrome.h include/PCSC/pcsc-nacl.h clean: - rm -rf workdir-* - rm -f lib/libcackey.a lib/libpcsc.a lib/libz.a - -rmdir lib rm -f cackey-chrome.o cackey-chrome-init.o rm -f cackey.pexe + rm -f cackey.crx cackey.nmf distclean: clean + rm -f lib/libcackey.a lib/libpcsc.a lib/libz.a + -rmdir lib + rm -f include/PCSC/pcsc-nacl.h + -rmdir include/PCSC + -rmdir include + rm -f google-pcsc.js + rm -rf workdir-* .PHONY: all clean distclean Index: build/chrome/build-deps ================================================================== --- build/chrome/build-deps +++ build/chrome/build-deps @@ -227,13 +227,27 @@ cd "${workdir}" || exit 1 # Copy out PC/SC headers for later use mkdir -p "${instdir}/include/PCSC" || exit 1 cp third_party/pcsc-lite/src/src/PCSC/*.h "${instdir}/include/PCSC" || exit 1 + + # Copy out extra headers + cat << \_EOF_ > "${instdir}/include/PCSC/pcsc-nacl.h" +#ifndef PCSC_NACL_H +#define PCSC_NACL_H 1 +#ifdef __cplusplus +#include +#include + +void pcscNaClInit(pp::Instance *instance, pp::Core *core); +#endif +#endif +_EOF_ # Copy out JavaScript files for later use mkdir "${instdir}/js" || exit 1 + cp common-utils/*.js "${instdir}/js" || exit 1 cp third_party/pcsc-lite/client-side/*.js "${instdir}/js" || exit 1 # Build libpcsc ## Assemble all the files into a single tree for file in logging.h scard_structs_serialization.h dom_requests_manager.h thread_safe_string_pool.h \ @@ -245,18 +259,20 @@ cat << \_EOF_ > third_party/pcsc-lite/client-side/Makefile || exit 1 CFLAGS += -Wall -std=gnu++11 CXXFLAGS += -Wall -std=gnu++11 CPPFLAGS += -I../src/src/PCSC -OBJS = pcsc_nacl.o pcsc_nacl_global.o scard_structs_serialization.o pp_var_utils.o logging.o dom_requests_manager.o +OBJS = pcsc_nacl.o pcsc_nacl_global.o scard_structs_serialization.o pp_var_utils.o logging.o dom_requests_manager.o pcsc_nacl_init.o all: libpcsc.a libpcsc.a: $(OBJS) rm -f libpcsc.a $(AR) rc libpcsc.a $(OBJS) -$(RANLIB) libpcsc.a + +pcsc_nacl_init.o: pcsc_nacl_init.cc pcsc_nacl_global.h dom_requests_manager.h pcsc_nacl.h pcsc_nacl.o: pcsc_nacl.cc pcsc_nacl.h logging.h pp_var_utils.h scard_structs_serialization.h pcsc_nacl.h: dom_requests_manager.h thread_safe_string_pool.h pcsc_nacl_global.o: pcsc_nacl_global.cc pcsc_nacl_global.h logging.h @@ -278,10 +294,49 @@ distclean: clean .PHONY: all clean distclean _EOF_ + + ## Create initialization procedure, since it lacks one + cat << \_EOF_ > third_party/pcsc-lite/client-side/pcsc_nacl_init.cc +#include +#include + +#include + +#include "pcsc_nacl_global.h" +#include "dom_requests_manager.h" +#include "pcsc_nacl.h" + +void pcscNaClInit(pp::Instance *instance, pp::Core *core) { + DomRequestsManager::PpDelegateImpl *drmDelegateImpl; + PcscNacl *pcsc_nacl; + + fprintf(stderr, "Called pcscNaClInit()\n"); + + drmDelegateImpl = new DomRequestsManager::PpDelegateImpl(instance, core); + + pcsc_nacl = new PcscNacl(new DomRequestsManager("pcsc-nacl", drmDelegateImpl), "nahamlodapjneockbbogcleglpoillcn", "client"); + + fprintf(stderr, "New PcscNacl object = %p\n", pcsc_nacl); + + if (!pcsc_nacl->Initialize()) { + fprintf(stderr, "PcscNacl->Initialize() failed !"); + + return; + } + + fprintf(stderr, "Global instance set to %p\n", pcsc_nacl); + + SetPcscNaclGlobalInstance(pcsc_nacl); + + fprintf(stderr, "Returning.\n"); + + return; +} +_EOF_ ## Make it make -C third_party/pcsc-lite/client-side || exit 1 # Install the built libpcsc Index: build/chrome/cackey-chrome-init.cc ================================================================== --- build/chrome/cackey-chrome-init.cc +++ build/chrome/cackey-chrome-init.cc @@ -1,17 +1,140 @@ -#include "ppapi/cpp/module.h" +/* + * Google's PCSC library requires us to write our module in C++ (thanks, Google) + * This library wraps the actual library, written in C. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pcsc-nacl.h" +#include "cackey-chrome.h" + +class CACKeyInstance : public pp::Instance { + private: + void pcscNaClInitWrapper(pp::Core *core) { + fprintf(stderr, "Calling pcscNaClInit(%p, %p)\n", this, core); + + pcscNaClInit(this, core); + + fprintf(stderr, "pcscNaClInit terminated\n"); + } + public: + explicit CACKeyInstance(PP_Instance instance, pp::Core *core) : pp::Instance(instance) { + std::thread(&CACKeyInstance::pcscNaClInitWrapper, this, core).detach(); + } + + virtual ~CACKeyInstance() {} + + virtual void HandleMessage(const pp::Var& messagePlain) { + int numCertificates, i; + struct cackey_certificate *certificates; + pp::VarDictionary *message; + pp::VarDictionary *reply; + pp::VarArray certificatesPPArray; + pp::VarArrayBuffer *certificateContents; + pp::Var command, target; + + /* + * The incoming message must be a dictionary + */ + if (!messagePlain.is_dictionary()) { + return; + } + + /* + * Process the appropriate command from the incoming message + */ + message = new pp::VarDictionary(messagePlain); + + /* + * Verify that this message is destined for us + */ + if (!message->HasKey("target")) { + return; + } + + target = message->Get("target"); + if (target.AsString() != "cackey") { + return; + } + + /* + * Determine what we are being asked to do + */ + if (!message->HasKey("command")) { + return; + } + command = message->Get("command"); + + /* + * Do the thing we are being asked to do + */ + if (command.AsString() == "listcertificates") { + numCertificates = cackey_chrome_listCertificates(&certificates); + + reply = new pp::VarDictionary(); + + certificatesPPArray.SetLength(numCertificates); + + for (i = 0; i < numCertificates; i++) { + certificateContents = new pp::VarArrayBuffer(certificates[i].certificate_len); + + memcpy(certificateContents->Map(), certificates[i].certificate, certificates[i].certificate_len); + + certificateContents->Unmap(); + + certificatesPPArray.Set(i, *certificateContents); + } + + reply->Set("status", "success"); + reply->Set("certificates", certificatesPPArray); + } else if (command.AsString() == "sign") { + reply = new pp::VarDictionary(); + + reply->Set("status", "success"); + } else { + reply = new pp::VarDictionary(); + + reply->Set("status", "error"); + reply->Set("error", "Invalid command"); + } + + /* + * If a message ID was sent in the request, include it in the reply + */ + if (message->HasKey("id")) { + reply->Set("id", message->Get("id")); + } + + /* + * Send the reply back to the requestor, hopefully they are waiting for this message + */ + PostMessage(*reply); + + return; + } +}; class CACKeyModule : public pp::Module { public: - CACKeyModule(): pp::Module() {} + CACKeyModule() : pp::Module() {} virtual ~CACKeyModule() {} virtual pp::Instance *CreateInstance(PP_Instance instance) { - return(NULL); + return(new CACKeyInstance(instance, core())); } }; namespace pp { Module *CreateModule() { - return(NULL); + return(new CACKeyModule()); } } ADDED build/chrome/cackey-chrome-test.c Index: build/chrome/cackey-chrome-test.c ================================================================== --- /dev/null +++ build/chrome/cackey-chrome-test.c @@ -0,0 +1,14 @@ +#include + +#include "cackey-chrome.h" + +int main(int argc, char **argv) { + struct cackey_certificate *certificates; + int numCertificates; + + numCertificates = cackey_chrome_listCertificates(&certificates); + + printf("numCertificates = %i\n", numCertificates); + + return(0); +} Index: build/chrome/cackey-chrome.c ================================================================== --- build/chrome/cackey-chrome.c +++ build/chrome/cackey-chrome.c @@ -1,4 +1,169 @@ -void C_GetFunctionList(void); -int main(int argc, char **argv) { - C_GetFunctionList(); +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "mypkcs11.h" +#include "cackey-chrome.h" + +struct cackey_chrome_id { + unsigned char *id; + size_t idLen; +}; + +static CK_FUNCTION_LIST_PTR moduleFunctionList = NULL; + +static CK_RV cackey_chrome_init(void) { + CK_C_INITIALIZE_ARGS initargs; + CK_RV chk_rv; + + if (moduleFunctionList != NULL) { + return(CKR_OK); + } + + chk_rv = C_GetFunctionList(&moduleFunctionList); + if (chk_rv != CKR_OK) { + return(chk_rv); + } + + initargs.CreateMutex = NULL; + initargs.DestroyMutex = NULL; + initargs.LockMutex = NULL; + initargs.UnlockMutex = NULL; + initargs.flags = CKF_OS_LOCKING_OK; + initargs.pReserved = NULL; + + chk_rv = moduleFunctionList->C_Initialize(&initargs); + if (chk_rv != CKR_OK) { + 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; + CK_SLOT_INFO slotInfo; + CK_SESSION_HANDLE hSession; + CK_OBJECT_HANDLE hObject; + CK_ULONG ulObjectCount; + CK_ATTRIBUTE searchTemplatePrivateKeys[] = { + {CKA_CLASS, NULL, sizeof(CK_OBJECT_CLASS)} + }; + CK_ATTRIBUTE searchTemplateCertificates[] = { + {CKA_CLASS, NULL, sizeof(CK_OBJECT_CLASS)}, + {CKA_ID, NULL, 0} + }; + CK_ATTRIBUTE attrTemplate[] = { + {CKA_ID, NULL, 0} + }, *currAttr; + CK_ULONG currAttrIndex; + CK_OBJECT_CLASS objectClassPrivateKey = CKO_PRIVATE_KEY; + + chk_rv = cackey_chrome_init(); + if (chk_rv != CKR_OK) { + return(0); + } + + chk_rv = moduleFunctionList->C_GetSlotList(FALSE, NULL, &numSlots); + if (chk_rv != CKR_OK) { + return(0); + } + + slots = malloc(sizeof(*slots) * numSlots); + + chk_rv = moduleFunctionList->C_GetSlotList(FALSE, slots, &numSlots); + if (chk_rv != CKR_OK) { + return(0); + } + + searchTemplatePrivateKeys[0].pValue = &objectClassPrivateKey; + + 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, searchTemplatePrivateKeys, sizeof(searchTemplatePrivateKeys) / sizeof(searchTemplatePrivateKeys[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; + } + + if (ulObjectCount == 0) { + break; + } + + 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); +} + +#ifdef __cplusplus } +#endif ADDED build/chrome/cackey-chrome.h Index: build/chrome/cackey-chrome.h ================================================================== --- /dev/null +++ build/chrome/cackey-chrome.h @@ -0,0 +1,21 @@ +#ifndef CACKEY_CHROME_CACKEY_H +#define CACKEY_CHROME_CACKEY_H 1 + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +struct cackey_certificate { + size_t certificate_len; + unsigned char *certificate; +}; + +int cackey_chrome_listCertificates(struct cackey_certificate **certificates); + +# ifdef __cplusplus +} +# endif + +#endif Index: build/chrome/cackey.js ================================================================== --- build/chrome/cackey.js +++ build/chrome/cackey.js @@ -297,10 +297,44 @@ function onCertificatesRejected(rejectedCerts) { // If certificates were rejected by the API, log an error, for example. console.error(rejectedCerts.length + ' certificates were rejected.'); return; } + +var cackeyHandle = null; + +function cackeyInitLoaded(messageEvent) { + console.log("Loaded CACKey PNaCl Module"); + + /* Register listeners with Chrome */ + chrome.certificateProvider.onCertificatesRequested.addListener(cackeyListCertificates); + chrome.certificateProvider.onSignDigestRequested.addListener(cackeySignMessage); +} + +function cackeyInit() { + var elementEmbed; + + if (cackeyHandle != null) { + return; + } + + elementEmbed = document.createElement('embed'); + elementEmbed.type = "application/x-pnacl"; + elementEmbed.width = 0; + elementEmbed.height = 0; + elementEmbed.src = "cackey.nmf"; + elementEmbed.id = "cackeyModule"; + elementEmbed.addEventListener('error', function(messageEvent) { console.error("Error loading CACKey PNaCl Module: " + messageEvent.data); }, true); + elementEmbed.addEventListener('load', cackeyInitLoaded, true); + elementEmbed.addEventListener('message', function(messageEvent) { console.log("Start message"); console.log(messageEvent.data); console.log("End message"); }, true); + + new GoogleSmartCard.PcscNacl(elementEmbed); + + document.body.appendChild(elementEmbed) + + cackeyHandle = elementEmbed; +} function cackeyListCertificates(chromeCallback) { var certificates = []; certificates.push( @@ -386,8 +420,10 @@ chromeCallback(payload); return; } -/* Register listeners with Chrome */ -chrome.certificateProvider.onCertificatesRequested.addListener(cackeyListCertificates); -chrome.certificateProvider.onSignDigestRequested.addListener(cackeySignMessage); +/* Enable debugging */ +GoogleSmartCard.logger.setLevel(GoogleSmartCard.Logger.prototype.DEBUG); + +/* Initialize CACKey */ +cackeyInit(); Index: build/chrome/manifest.json ================================================================== --- build/chrome/manifest.json +++ build/chrome/manifest.json @@ -1,13 +1,18 @@ { "manifest_version": 2, "name": "CACKey", "version": "0.7.5.1", - "background": { - "scripts": ["cackey.js"], - "persistent": false + "app": { + "background": { + "scripts": [ + "google-pcsc.js", + "cackey.js" + ], + "persistent": false + } }, "permissions": [ "certificateProvider", "usb"