// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @fileoverview CryptoToken background page */ 'use strict'; /** @const */ var HTTP_ORIGINS_ALLOWED = false; /** @const */ var LOG_SAVER_EXTENSION_ID = 'fjajfjhkeibgmiggdfehjplbhmfkialk'; var LOG_SAVER_EXTENSION_ORIGIN = 'chrome-extension://' + LOG_SAVER_EXTENSION_ID; var FACTORY_REGISTRY = (function() { var windowTimer = new WindowTimer(); var xhrTextFetcher = new XhrTextFetcher(); return new FactoryRegistry( new XhrAppIdCheckerFactory(xhrTextFetcher), new CryptoTokenApprovedOrigin(), new CountdownTimerFactory(windowTimer), new CryptoTokenOriginChecker(), windowTimer); })(); /** * @param {*} request The received request * @return {boolean} Whether the request is a register/enroll request. */ function isRegisterRequest(request) { if (!request) { return false; } switch (request.type) { case MessageTypes.U2F_REGISTER_REQUEST: return true; default: return false; } } /** * Default response callback to deliver a response to a request. * @param {*} request The received request. * @param {function(*): void} sendResponse A callback that delivers a response. * @param {*} response The response to return. */ function defaultResponseCallback(request, sendResponse, response) { response['requestId'] = request['requestId']; try { sendResponse(response); } catch (e) { console.warn(UTIL_fmt('caught: ' + e.message)); } } /** * Response callback that delivers a response to a request only when the * sender is a foreground tab. * @param {*} request The received request. * @param {!MessageSender} sender The message sender. * @param {function(*): void} sendResponse A callback that delivers a response. * @param {*} response The response to return. */ function sendResponseToActiveTabOnly(request, sender, sendResponse, response) { // For WebAuthn-proxied requests on Windows, dismissing the native Windows // UI after a timeout races with the error being returned here. Hence, skip // the focus check for all timeouts. if (response.responseData && response.responseData.errorCode == ErrorCodes.TIMEOUT) { defaultResponseCallback(request, sendResponse, response); return; } tabInForeground(sender.tab.id).then(function(result) { // If the tab is no longer in the foreground, drop the result: the user // is no longer interacting with the tab that originated the request. if (result) { defaultResponseCallback(request, sendResponse, response); } }); } /** * Common handler for messages received from chrome.runtime.sendMessage and * chrome.runtime.connect + postMessage. * @param {*} request The received request * @param {!MessageSender} sender The message sender * @param {function(*): void} sendResponse A callback that delivers a response * @return {Closeable} A Closeable request handler. */ function messageHandler(request, sender, sendResponse) { var responseCallback; if (isRegisterRequest(request)) { responseCallback = sendResponseToActiveTabOnly.bind(null, request, sender, sendResponse); } else { responseCallback = defaultResponseCallback.bind(null, request, sendResponse); } var closeable = handleWebPageRequest( /** @type {Object} */ (request), sender, responseCallback); return closeable; } /** * Listen to individual messages sent from (allowlisted) extensions/apps via * chrome.runtime.sendMessage * @param {*} request The received request * @param {!MessageSender} sender The message sender * @param {function(*): void} sendResponse A callback that delivers a response * @return {boolean} */ function messageHandlerExternal(request, sender, sendResponse) { if (sender.origin && sender.origin === LOG_SAVER_EXTENSION_ORIGIN) { return handleLogSaverMessage(request); } messageHandler(request, sender, sendResponse); return true; // Tell Chrome not to destroy sendResponse yet } chrome.runtime.onMessageExternal.addListener(messageHandlerExternal); // Listen to direct connection events, and wire up a message handler on the port chrome.runtime.onConnectExternal.addListener(function(port) { function sendResponse(response) { port.postMessage(response); } var closeable; port.onMessage.addListener(function(request) { var sender = /** @type {!MessageSender} */ (port.sender); closeable = messageHandler(request, sender, sendResponse); }); port.onDisconnect.addListener(function() { if (closeable) { closeable.close(); } }); }); /** * Handles messages from the log-saver app. Temporarily replaces UTIL_fmt with * a wrapper that also sends formatted messages to the app. * @param {*} request The message received from the app * @return {boolean} Used as chrome.runtime.onMessage handler return value */ function handleLogSaverMessage(request) { if (request === 'start') { if (originalUtilFmt) { // We're already sending return false; } originalUtilFmt = UTIL_fmt; UTIL_fmt = function(s) { var line = originalUtilFmt(s); chrome.runtime.sendMessage(LOG_SAVER_EXTENSION_ID, line); return line; }; } else if (request === 'stop') { if (originalUtilFmt) { UTIL_fmt = originalUtilFmt; originalUtilFmt = null; } } return false; } /** @private */ var originalUtilFmt = null;