/*
* Google's PCSC library requires us to write our module in C++ to initialize
* it. This component handles the initialization of our module and handles
* incoming messages, passing them either to our library (cackey-chrome) or
* to the PCSC-NaCl library from Google as appropriate.
*/
#include <thread>
#include <ppapi/cpp/instance.h>
#include <ppapi/cpp/module.h>
#include <ppapi/cpp/core.h>
#include <ppapi/cpp/var.h>
#include <ppapi/cpp/var_dictionary.h>
#include <ppapi/cpp/var_array.h>
#include <ppapi/cpp/var_array_buffer.h>
#include <string.h>
#include <stdlib.h>
#include "pcsc-nacl.h"
#include "cackey-chrome.h"
class CACKeyInstance : public pp::Instance {
private:
pp::Core *corePointer;
public:
explicit CACKeyInstance(PP_Instance instance, pp::Core *core) : pp::Instance(instance) {
corePointer = core;
}
virtual ~CACKeyInstance() {}
virtual void HandleMessageThread(pp::VarDictionary *message) {
cackey_chrome_returnType signRet;
char *pinPrompt = NULL;
const char *pin;
const char *smartcardManagerAppId = NULL;
unsigned char buffer[8192];
struct cackey_certificate *certificates, incomingCertificateCACKey;
pp::VarDictionary *reply;
pp::VarArray certificatesPPArray;
pp::VarArrayBuffer *certificateContents, *incomingCertificateContents, *incomingData, *outgoingData;
pp::Var command;
const pp::Var *messageAsVar = NULL, *outgoingDataAsVar = NULL;
int numCertificates, i;
unsigned long outgoingDataLength;
/*
* Extract the command
*/
command = message->Get("command");
/*
* Do the thing we are being asked to do
*/
reply = new pp::VarDictionary();
if (command.AsString() == "init") {
if (message->HasKey("smartcardManagerAppId")) {
smartcardManagerAppId = strdup(message->Get("smartcardManagerAppId").AsString().c_str());
}
pcscNaClInit(this, corePointer, smartcardManagerAppId, "CACKey");
if (smartcardManagerAppId) {
free((void *) smartcardManagerAppId);
}
reply->Set("status", "success");
} else if (command.AsString() == "listcertificates") {
numCertificates = cackey_chrome_listCertificates(&certificates);
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);
delete certificateContents;
}
cackey_chrome_freeCertificates(certificates, numCertificates);
reply->Set("status", "success");
reply->Set("certificates", certificatesPPArray);
} else if (command.AsString() == "sign") {
if (!message->HasKey("certificate")) {
reply->Set("status", "error");
reply->Set("error", "Certificate not supplied");
} else if (!message->HasKey("data")) {
reply->Set("status", "error");
reply->Set("error", "Data not supplied");
} else {
incomingCertificateContents = new pp::VarArrayBuffer(message->Get("certificate"));
incomingData = new pp::VarArrayBuffer(message->Get("data"));
if (message->HasKey("pin")) {
pin = message->Get("pin").AsString().c_str();
} else {
pin = NULL;
}
incomingCertificateCACKey.certificate = incomingCertificateContents->Map();
incomingCertificateCACKey.certificate_len = incomingCertificateContents->ByteLength();
outgoingDataLength = sizeof(buffer);
signRet = cackey_chrome_signMessage(&incomingCertificateCACKey,
incomingData->Map(), incomingData->ByteLength(),
buffer, &outgoingDataLength,
&pinPrompt, pin
);
incomingCertificateContents->Unmap();
incomingData->Unmap();
delete incomingCertificateContents;
delete incomingData;
switch (signRet) {
case CACKEY_CHROME_OK:
outgoingData = new pp::VarArrayBuffer(outgoingDataLength);
memcpy(outgoingData->Map(), buffer, outgoingDataLength);
outgoingData->Unmap();
outgoingDataAsVar = new pp::Var(outgoingData->pp_var());
delete outgoingData;
reply->Set("status", "success");
reply->Set("signedData", outgoingDataAsVar);
delete outgoingDataAsVar;
break;
case CACKEY_CHROME_ERROR:
reply->Set("status", "error");
reply->Set("error", "Unable to sign data");
break;
case CACKEY_CHROME_NEEDLOGIN:
case CACKEY_CHROME_NEEDPROTECTEDLOGIN:
messageAsVar = new pp::Var(message->pp_var());
reply->Set("status", "retry");
reply->Set("originalrequest", messageAsVar);
reply->Set("pinprompt", pinPrompt);
delete messageAsVar;
break;
}
if (pinPrompt != NULL) {
free(pinPrompt);
}
}
} else {
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"));
}
/*
* Indicate who our message is for
*/
reply->Set("target", "cackey");
reply->Set("command", command);
/*
* Send the reply back to the requestor, hopefully they are waiting for this message
*/
PostMessage(*reply);
delete reply;
delete message;
return;
}
virtual void HandleMessage(const pp::Var& messagePlain) {
pp::VarDictionary *message;
pp::Var target;
/*
* The incoming message must be a dictionary
*/
if (!messagePlain.is_dictionary()) {
pcscNaClHandleMessage(messagePlain);
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")) {
delete message;
/* We don't handle this message, see if PCSC-NaCl does */
pcscNaClHandleMessage(messagePlain);
return;
}
target = message->Get("target");
if (target.AsString() != "cackey") {
delete message;
/* We don't handle this message, see if PCSC-NaCl does */
pcscNaClHandleMessage(messagePlain);
return;
}
/*
* Determine what we are being asked to do
*/
if (!message->HasKey("command")) {
delete message;
/* We don't handle this message, see if PCSC-NaCl does */
pcscNaClHandleMessage(messagePlain);
return;
}
/*
* Process the request in another thread
*/
std::thread(&CACKeyInstance::HandleMessageThread, this, message).detach();
return;
}
};
class CACKeyModule : public pp::Module {
public:
CACKeyModule() : pp::Module() {}
virtual ~CACKeyModule() {}
virtual pp::Instance *CreateInstance(PP_Instance instance) {
return(new CACKeyInstance(instance, core()));
}
};
namespace pp {
Module *CreateModule() {
return(new CACKeyModule());
}
}