Index: build/chrome/cackey.js ================================================================== --- build/chrome/cackey.js +++ build/chrome/cackey.js @@ -6,10 +6,18 @@ // If certificates were rejected by the API, log an error, for example. console.error(rejectedCerts.length + ' certificates were rejected.'); return; } +/* + * Optional features for CACKey + */ +var cackeyFeatures = { + customPINPrompt: true, + useChromePINDialog: true +}; + /* * Handle for the CACKey NaCl Target */ var cackeyHandle = null; var cackeyPCSCHandle = null; @@ -177,10 +185,64 @@ cackeyCertificateToPINMapUpdateLastUsed(null); }, 900000); } } + +/* + * Handle redispatching requests after receiving a PIN + */ +function cackeyPINPromptCompleted(pinValue) { + var tmpMessageEvent; + + for (messageIdx = 0; messageIdx < cackeyMessagesToRetry.length; messageIdx++) { + tmpMessageEvent = cackeyMessagesToRetry[messageIdx]; + + if (pinValue == "") { + if (goog.DEBUG) { + console.log("[cackey] The PIN dialog was closed without gathering a PIN, treating it as a failure."); + } + + tmpMessageEvent.data.status = "error"; + tmpMessageEvent.data.error = "PIN window closed without a PIN being provided"; + + cackeyMessageIncoming(tmpMessageEvent); + } else { + tmpMessageEvent.data.originalrequest.pin = pinValue; + + cackeyCertificateToPINMap[cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)] = {} + cackeyCertificateToPINMap[cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)].pin = pinValue; + cackeyCertificateToPINMapUpdateLastUsed(cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)); + + chromeCallback = null; + if (tmpMessageEvent.data.id) { + if (cackeyOutstandingCallbacks) { + chromeCallback = cackeyOutstandingCallbacks[tmpMessageEvent.data.id]; + } + } + + cackeyInitPCSC(function() { + cackeyHandle.postMessage(tmpMessageEvent.data.originalrequest); + }, function() { + if (chromeCallback) { + chromeCallback(); + } + + if (tmpMessageEvent.data.id && cackeyOutstandingCallbacks[tmpMessageEvent.data.id]) { + delete cackeyOutstandingCallbacks[tmpMessageEvent.data.id]; + } + }); + } + } + + /* + * Delete the existing handle and create a new one + */ + delete cackeyMessagesToRetry; + + cackeyMessagesToRetry = []; +} /* * Handle an incoming message from the NaCl side and pass it off to * one of the handlers above for actual formatting and passing to * the callback @@ -260,116 +322,99 @@ * Set the handle to an invalid (but non-null) value until the window * is created in case we are invoked again soon. */ pinWindowPreviousHandle = "invalid"; - chrome.app.window.create("pin.html", { - "id": "cackeyPINEntry", - "resizable": false, - "alwaysOnTop": true, - "focused": true, - "visibleOnAllWorkspaces": true, - "innerBounds": { - "width": 350, - "minWidth": 350, - "height": 135, - "minHeight": 135 - } - }, function(pinWindow) { - /* - * Set the PIN value to blank - */ - pinWindowPINValue = ""; - - if (!pinWindow) { - console.error("[cackey] No window was provided for PIN entry, this will not go well."); - + if (messageEvent.data.originalrequest.signRequestId === null || cackeyFeatures.useChromePINDialog === false) { + chrome.app.window.create("pin.html", { + "id": "cackeyPINEntry", + "resizable": false, + "alwaysOnTop": true, + "focused": true, + "visibleOnAllWorkspaces": true, + "innerBounds": { + "width": 350, + "minWidth": 350, + "height": 135, + "minHeight": 135 + } + }, function(pinWindow) { + /* + * Set the PIN value to blank + */ + pinWindowPINValue = ""; + + if (!pinWindow) { + console.error("[cackey] No window was provided for PIN entry, this will not go well."); + + return; + } + + pinWindowPreviousHandle = pinWindow; + + pinWindow.drawAttention(); + pinWindow.focus(); + + /* + * Register a handler to handle the window being closed without + * having sent anything + */ + pinWindow.onClosed.addListener(function() { + var messageIdx; + var chromeCallback; + var pinValue; + + pinWindowPreviousHandle = null; + + /* + * We are done fetching the user PIN, clear the value + */ + pinValue = pinWindowPINValue; + pinWindowPINValue = ""; + + cackeyPINPromptCompleted(pinValue); + + return; + }) + + /* + * Pass this message off to the other window so that it may resubmit the request. + */ + pinWindow.contentWindow.parentWindow = window; + pinWindow.contentWindow.messageEvent = messageEvent; + + if (cackeyFeatures.customPINPrompt) { + pinWindow.contentWindow.pinprompt = messageEvent.data.pinprompt; + } + return; - } - - pinWindowPreviousHandle = pinWindow; - - pinWindow.drawAttention(); - pinWindow.focus(); - - /* - * Register a handler to handle the window being closed without - * having sent anything - */ - pinWindow.onClosed.addListener(function() { - var messageIdx; - var chromeCallback; + }); + } else { + chrome.certificateProvider.requestPin({ + signRequestId: messageEvent.data.originalrequest.signRequestId, + requestType: "PIN" + }, function(userInfo) { + var destroyError; + var pinValue = ""; + + try { + chrome.certificateProvider.stopPinRequest({ + signRequestId: messageEvent.data.originalrequest.signRequestId + }, function() {}); + } catch (destroyError) { + /* Do nothing, we don't care if it fails really */ + } pinWindowPreviousHandle = null; - for (messageIdx = 0; messageIdx < cackeyMessagesToRetry.length; messageIdx++) { - var tmpMessageEvent; - - tmpMessageEvent = cackeyMessagesToRetry[messageIdx]; - - if (pinWindowPINValue == "") { - if (goog.DEBUG) { - console.log("[cackey] The PIN dialog was closed without gathering a PIN, treating it as a failure."); - } - - tmpMessageEvent.data.status = "error"; - tmpMessageEvent.data.error = "PIN window closed without a PIN being provided"; - - cackeyMessageIncoming(tmpMessageEvent); - } else { - tmpMessageEvent.data.originalrequest.pin = pinWindowPINValue; - - cackeyCertificateToPINMap[cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)] = {} - cackeyCertificateToPINMap[cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)].pin = pinWindowPINValue; - - cackeyCertificateToPINMapUpdateLastUsed(cackeyCertificateToPINID(tmpMessageEvent.data.originalrequest.certificate)); - - chromeCallback = null; - if (tmpMessageEvent.data.id) { - if (cackeyOutstandingCallbacks) { - chromeCallback = cackeyOutstandingCallbacks[tmpMessageEvent.data.id]; - } - } - - cackeyInitPCSC(function() { - cackeyHandle.postMessage(tmpMessageEvent.data.originalrequest); - }, function() { - if (chromeCallback) { - chromeCallback(); - } - - if (tmpMessageEvent.data.id && cackeyOutstandingCallbacks[tmpMessageEvent.data.id]) { - delete cackeyOutstandingCallbacks[tmpMessageEvent.data.id]; - } - }); - } - } - - - /* - * Delete the existing handle and create a new one - */ - delete cackeyMessagesToRetry; - - cackeyMessagesToRetry = []; - - /* - * We are done fetching the user PIN, clear the value - */ - pinWindowPINValue = ""; - - return; - }) - - /* - * Pass this message off to the other window so that it may resubmit the request. - */ - pinWindow.contentWindow.parentWindow = window; - pinWindow.contentWindow.messageEvent = messageEvent; - - return; - }); + if (userInfo && userInfo.userInput) { + pinValue = userInfo.userInput; + } + + return(cackeyPINPromptCompleted(pinValue)); + }); + } /* * We return here instead of break to avoid deleting the callback * entry. */ @@ -486,10 +531,14 @@ var callbackId; var command; var certificateId; var digest, digestHeader; var promiseHandle = null, promiseResolve, promiseReject; + + if (signRequest.signRequestId === undefined || signRequest.signRequestId === null) { + signRequest.signRequestId = null; + } if (!chromeCallback) { /* * If no callback supplied, arrange for a promise to be returned instead */ @@ -545,10 +594,11 @@ callbackId = ++cackeyOutstandingCallbackCounter; command = { 'target': "cackey", 'command': "sign", + 'signRequestId': signRequest.signRequestId, 'id': callbackId, 'certificate': signRequest.certificate, 'data': digest.buffer }; Index: build/chrome/manifest.json.in ================================================================== --- build/chrome/manifest.json.in +++ build/chrome/manifest.json.in @@ -1,17 +1,14 @@ { "manifest_version": 2, "name": "CACKey", "short_name": "CACKey", - "version": "@PACKAGE_VERSION@.10", - + "version": "@PACKAGE_VERSION@.11", "description": "US Department of Defense Common Access Card (CAC) and US NIST SP 800-73 Personal Identity Verification (PIV) card support.", - "icons": { "128": "icon.png" }, - "app": { "background": { "scripts": [ "google-pcsc.js", "jsrsasign.js", @@ -20,11 +17,10 @@ "cackey.js" ], "persistent": false } }, - "permissions": [ "certificateProvider", "storage", "alwaysOnTopWindows" ], Index: build/chrome/ssh-agent.js ================================================================== --- build/chrome/ssh-agent.js +++ build/chrome/ssh-agent.js @@ -9,11 +9,11 @@ /* * XXX:TODO: Expose UI for this */ cackeySSHAgentFeatures = { - enabled: true, + enabled: false, includeKeys: true, includeCerts: true, legacy: false };