Index: build/chrome/cackey.js
==================================================================
--- build/chrome/cackey.js
+++ build/chrome/cackey.js
@@ -11,10 +11,11 @@
 /*
  * Handle for the CACKey NaCl Target
  */
 var cackeyHandle = null;
 var cackeyPCSCHandle = null;
+var cackeyPCSCHandleUsable = false;
 
 /*
  * Handle and ID for outstanding callbacks
  */
 var cackeyOutstandingCallbacks = {}
@@ -34,10 +35,15 @@
 /*
  * Stored PIN for a given certificate
  */
 var cackeyCertificateToPINMap = {};
 
+/*
+ * Callbacks to perform after PCSC comes online
+ */
+cackeyCallbackAfterInit = []
+
 /*
  * Compute a text-based handle for a certificate to be hashed by
  */
 function cackeyCertificateToPINID(certificate) {
 	var id;
@@ -67,11 +73,11 @@
 
 	for (idx = 0; idx < message.certificates.length; idx++) {
 		certificates.push(
 			{
 				certificate: message.certificates[idx],
-				supportedHashes: ['SHA1', 'SHA256']
+				supportedHashes: ['SHA1', 'SHA256', 'MD5_SHA1']
 			}
 		);
 	}
 
 	chromeCallback(certificates,
@@ -85,10 +91,12 @@
 			}
 
 			return;
 		}
 	);
+
+	return;
 }
 
 /*
  * Handle a response from the NaCl side regarding signing a message
  */
@@ -96,10 +104,12 @@
 	var payload;
 
 	payload = message.signedData;
 
 	chromeCallback(payload);
+
+	return;
 }
 
 /*
  * Handle an incoming message from the NaCl side and pass it off to
  * one of the handlers above for actual formatting and passing to
@@ -229,11 +239,13 @@
 						} else {
 							tmpMessageEvent.data.originalrequest.pin = pinWindowPINValue;
 
 							cackeyCertificateToPINMap[cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)] = pinWindowPINValue;
 
-							cackeyHandle.postMessage(tmpMessageEvent.data.originalrequest);
+							cackeyInitPCSC(function() {
+								cackeyHandle.postMessage(tmpMessageEvent.data.originalrequest);
+							});
 						}
 					}
 
 
 					/*
@@ -299,24 +311,26 @@
 		console.log("[cackey] Asked to provide a list of certificates -- throwing that request over to the NaCl side... ");
 	}
 
 	callbackId = cackeyOutstandingCallbackCounter + 1;
 
-	cackeyHandle.postMessage(
-		{
-			'target': "cackey",
-			'command': "listcertificates",
-			'id': callbackId
-		}
-	);
-
-	cackeyOutstandingCallbackCounter = callbackId;
-	cackeyOutstandingCallbacks[callbackId] = chromeCallback;
-
-	if (GoogleSmartCard.IS_DEBUG_BUILD) {
-		console.log("[cackey] Thrown.");
-	}
+	cackeyInitPCSC(function() {
+		cackeyHandle.postMessage(
+			{
+				'target': "cackey",
+				'command': "listcertificates",
+				'id': callbackId
+			}
+		);
+
+		cackeyOutstandingCallbackCounter = callbackId;
+		cackeyOutstandingCallbacks[callbackId] = chromeCallback;
+
+		if (GoogleSmartCard.IS_DEBUG_BUILD) {
+			console.log("[cackey] Thrown.");
+		}
+	});
 
 	return;
 }
 
 /*
@@ -373,54 +387,85 @@
 
 	if (cackeyCertificateToPINMap[certificateId]) {
 		command.pin = cackeyCertificateToPINMap[certificateId];
 	}
 
-	cackeyHandle.postMessage(command);
-
-	cackeyOutstandingCallbackCounter = callbackId;
-	cackeyOutstandingCallbacks[callbackId] = chromeCallback;
-
-	if (GoogleSmartCard.IS_DEBUG_BUILD) {
-		console.log("[cackey] Thrown.");
-	}
+	cackeyInitPCSC(function() {
+		cackeyHandle.postMessage(command);
+
+		cackeyOutstandingCallbackCounter = callbackId;
+		cackeyOutstandingCallbacks[callbackId] = chromeCallback;
+
+		if (GoogleSmartCard.IS_DEBUG_BUILD) {
+			console.log("[cackey] Thrown.");
+		}
+	});
+
+	return;
+}
+
+/*
+ * Unititalizes the CACKey PCSC connection
+ */
+function cackeyUninitPCSC() {
+	console.log("[cackey] cackeyUninitPCSC() called");
+
+	if (cackeyPCSCHandle != null) {
+		console.log("[cackey] Deleting PCSC handle");
+
+		delete cackeyPCSCHandle;
+
+		cackeyPCSCHandle = null;
+	}
+
+	cackeyPCSCHandleUsable = false;
+
+	console.log("[cackey] cackeyUninitPCSC() returning");
 
 	return;
 }
 
 /*
  * Uninitializes CACKey (probably due to a crash)
  */
 function cackeyUninit() {
+	console.log("[cackey] cackeyUninit() called");
+
 	if (chrome.certificateProvider) {
+		console.log("[cackey] Unregistered Chrome certificate handlers");
+
 		chrome.certificateProvider.onCertificatesRequested.removeListener(cackeyListCertificates);
 		chrome.certificateProvider.onSignDigestRequested.removeListener(cackeySignMessage);
 	}
 
-	if (cackeyPCSCHandle != null) {
-		delete cackeyPCSCHandle;
-
-		cackeyPCSCHandle = null;
-	}
+	cackeyUninitPCSC();
 
 	if (cackeyHandle != null) {
+		console.log("[cackey] Deleting PNaCl module");
+
 		try {
 			document.body.removeChild(cackeyHandle);
 		} catch (e) { }
 
 		delete cackeyHandle;
 
 		cackeyHandle = null;
 	}
+
+	console.log("[cackey] cackeyUninit() complete");
+
+	return;
 }
 
 /*
  * Restarts CACKey
  */
 function cackeyRestart() {
 	cackeyUninit();
 	cackeyInit();
+
+	return;
 }
 
 /*
  * Handle a CACKey crash (probably due to loss of connectivity to the PCSC daemon)
  */
@@ -428,22 +473,75 @@
 	/*
 	 * Schedule the restart to occur in 30 seconds in case we really are
 	 * not working.
 	 */
 	setTimeout(cackeyRestart, 30000);
+
+	return;
+}
+
+function cackeyInitPCSCCompleted() {
+	var idx;
+
+	cackeyPCSCHandleUsable = true;
+
+	for (idx = 0; idx < cackeyCallbackAfterInit.length; idx++) {
+		if (!cackeyCallbackAfterInit[idx]) {
+			continue;
+		}
+
+		cackeyCallbackAfterInit[idx]();
+	}
+
+	delete cackeyCallbackAfterInit;
+
+	cackeyCallbackAfterInit = [];
+
+	return;
 }
 
 /*
- * Finish performing initialization that must wait until we have loaded the CACKey module
+ * Initialize the PCSC connection
  */
-function cackeyInitLoaded(messageEvent) {
-	console.log("[cackey] Loaded CACKey PNaCl Module");
+function cackeyInitPCSC(callbackAfterInit) {
+	/*
+	 * Start the Google PCSC Interface
+	 */
+
+	console.log("[cackey] cackeyInitPCSC() called");
+
+	/*
+	 * Queue this callback to be completed when initialization is complete
+	 */
+	if (callbackAfterInit) {
+		cackeyCallbackAfterInit.push(callbackAfterInit);
+	}
+
+	/*
+	 * No additional work is required
+	 */
+
+	if (cackeyPCSCHandle) {
+		console.log("[cackey] PCSC handle is already valid, nothing to do.");
+
+		if (cackeyPCSCHandleUsable) {
+			cackeyInitPCSCCompleted();
+		}
+
+		return;
+	}
+
+	/*
+	 * Sanely initialize this
+	 */
+	cackeyPCSCHandleUsable = false;
 
-	/* Register listeners with Chrome */
-	if (chrome.certificateProvider) {
-		chrome.certificateProvider.onCertificatesRequested.addListener(cackeyListCertificates);
-		chrome.certificateProvider.onSignDigestRequested.addListener(cackeySignMessage);
+	/*
+	 * Initialize the CACKey PNaCl module if needed
+	 */
+	if (cackeyHandle == null) {
+		cackeyInit();
 	}
 
 	/*
 	 * Initialize CACKey with the correct handle to talk to the Google Smartcard Manager App
 	 */
@@ -454,13 +552,32 @@
 			"smartcardManagerAppId": "khpfeaanjngmcnplbdlpegiifgpfgdco"
 		}
 	);
 
 	/*
-	 * Start the Google PCSC Interface
+	 * Initialize the PCSC NaCl interface
 	 */
 	cackeyPCSCHandle = new GoogleSmartCard.PcscNacl(cackeyHandle);
+
+	console.log("[cackey] cackeyInitPCSC() complete");
+
+	return;
+}
+
+/*
+ * Finish performing initialization that must wait until we have loaded the CACKey module
+ */
+function cackeyInitLoaded(messageEvent) {
+	console.log("[cackey] Loaded CACKey PNaCl Module");
+
+	/* Register listeners with Chrome */
+	if (chrome.certificateProvider) {
+		console.log("[cackey] Registered Certificate handlers with Chrome");
+
+		chrome.certificateProvider.onCertificatesRequested.addListener(cackeyListCertificates);
+		chrome.certificateProvider.onSignDigestRequested.addListener(cackeySignMessage);
+	}
 
 	return;
 }
 
 /*
@@ -513,16 +630,21 @@
 	 * by requesting its position
 	 */
 	forceLoadElement = cackeyHandle.offsetTop;
 
 	console.log("[cackey] cackeyInit(): Completed.  Returning.");
+
+	return;
 }
 
 /*
  * Initialize the CACKey Chrome Application
  */
 function cackeyAppInit() {
+	var oldOnPortDisconnectedFunction;
+	var oldPCSCInitializationCallback;
+
 	/*
 	 * Create a handler for starting the application UI
 	 */
 	chrome.app.runtime.onLaunched.addListener(function() {
 		chrome.app.window.create('ui.html', {
@@ -534,10 +656,36 @@
 				"height": 135,
 				"minHeight": 135
 			}
 		});
 	});
+
+	/*
+	 * Register a handler for dealing with the PCSC port being disconnected
+	 */
+	oldOnPortDisconnectedFunction = GoogleSmartCard.Pcsc.prototype.onPortDisconnected_;
+	GoogleSmartCard.Pcsc.prototype.onPortDisconnected_ = function() {
+		oldOnPortDisconnectedFunction.apply(this);
+
+		cackeyRestart();
+
+		return;
+	};
+
+	/*
+	 * Register a handler for dealing with the PCSC port being available
+	 */
+	oldPCSCInitializationCallback = GoogleSmartCard.PcscNacl.prototype.pcscInitializationCallback_;
+	GoogleSmartCard.PcscNacl.prototype.pcscInitializationCallback_ = function(requestId, instanceId, instance, error) {
+		oldPCSCInitializationCallback.apply(this, [requestId, instanceId, instance, error]);
+
+		cackeyInitPCSCCompleted();
+
+		return;
+	};
+
+	return;
 }
 
 /* Initialize CACKey */
 cackeyAppInit();
 cackeyInit();