15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 The Chromium Authors. All rights reserved. 25c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Use of this source code is governed by a BSD-style license that can be 35c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// found in the LICENSE file. 45c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 55c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 65c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @fileoverview Handles web page requests for gnubby sign requests. 76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * 85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict'; 115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvar signRequestQueue = new OriginKeyedRequestQueue(); 135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Handles a web sign request. 165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {MessageSender} sender The sender of the message. 175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} request The web page's sign request. 185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function} sendResponse Called back with the result of the sign. 19010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @return {Closeable} Request handler that should be closed when the browser 20010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * message channel is closed. 215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function handleWebSignRequest(sender, request, sendResponse) { 235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var sentResponse = false; 246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var queuedSignRequest; 256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci function sendErrorResponse(error) { 276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendResponseOnce(sentResponse, queuedSignRequest, 286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) makeWebErrorResponse(request, 291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci mapErrorCodeToGnubbyCodeType(error.errorCode, true /* forSign */)), 306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendResponse); 316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) function sendSuccessResponse(challenge, info, browserData) { 346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var responseData = makeWebSignResponseDataFromChallenge(challenge); 356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) addSignatureAndBrowserDataToResponseData(responseData, info, browserData, 366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 'browserData'); 376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var response = makeWebSuccessResponse(request, responseData); 386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendResponseOnce(sentResponse, queuedSignRequest, response, sendResponse); 395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) queuedSignRequest = 426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) validateAndEnqueueSignRequest( 436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sender, request, 'signData', sendErrorResponse, 446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendSuccessResponse); 456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return queuedSignRequest; 466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Handles a U2F sign request. 506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {MessageSender} sender The sender of the message. 516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The web page's sign request. 526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Function} sendResponse Called back with the result of the sign. 536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Closeable} Request handler that should be closed when the browser 546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * message channel is closed. 556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function handleU2fSignRequest(sender, request, sendResponse) { 576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var sentResponse = false; 586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var queuedSignRequest; 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci function sendErrorResponse(error) { 616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendResponseOnce(sentResponse, queuedSignRequest, 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci makeU2fErrorResponse(request, error.errorCode, error.errorMessage), 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci sendResponse); 645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function sendSuccessResponse(challenge, info, browserData) { 676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var responseData = makeU2fSignResponseDataFromChallenge(challenge); 686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) addSignatureAndBrowserDataToResponseData(responseData, info, browserData, 696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 'clientData'); 706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var response = makeU2fSuccessResponse(request, responseData); 716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendResponseOnce(sentResponse, queuedSignRequest, response, sendResponse); 725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) queuedSignRequest = 756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) validateAndEnqueueSignRequest( 766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sender, request, 'signRequests', sendErrorResponse, 776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sendSuccessResponse); 786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return queuedSignRequest; 796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Creates a base U2F responseData object from the server challenge. 836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {SignChallenge} challenge The server challenge. 846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The responseData object. 856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeU2fSignResponseDataFromChallenge(challenge) { 876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var responseData = { 886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 'keyHandle': challenge['keyHandle'] 896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) }; 906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return responseData; 916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Creates a base web responseData object from the server challenge. 956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {SignChallenge} challenge The server challenge. 966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The responseData object. 976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeWebSignResponseDataFromChallenge(challenge) { 996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var responseData = {}; 1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (var k in challenge) { 1016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) responseData[k] = challenge[k]; 1026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return responseData; 1046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Adds the browser data and signature values to a responseData object. 1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} responseData The "base" responseData object. 1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} signatureData The signature data. 1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} browserData The browser data generated from the challenge. 1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} browserDataName The name of the browser data key in the 1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * responseData object. 1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function addSignatureAndBrowserDataToResponseData(responseData, signatureData, 1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) browserData, browserDataName) { 1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) responseData[browserDataName] = B64_encode(UTIL_StringToBytes(browserData)); 1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) responseData['signatureData'] = signatureData; 1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Validates a sign request using the given sign challenges name, and, if valid, 1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * enqueues the sign request for eventual processing. 1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {MessageSender} sender The sender of the message. 1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The web page's sign request. 1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} signChallengesName The name of the sign challenges value in 1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * the request. 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function(U2fError)} errorCb Error callback. 1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {function(SignChallenge, string, string)} successCb Success callback. 1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Closeable} Request handler that should be closed when the browser 1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * message channel is closed. 1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function validateAndEnqueueSignRequest(sender, request, 1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) signChallengesName, errorCb, successCb) { 1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var origin = getOriginFromUrl(/** @type {string} */ (sender.url)); 1355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!origin) { 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorCb({errorCode: ErrorCodes.BAD_REQUEST}); 1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return null; 1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // More closure type inference fail. 1405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var nonNullOrigin = /** @type {string} */ (origin); 1415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!isValidSignRequest(request, signChallengesName)) { 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorCb({errorCode: ErrorCodes.BAD_REQUEST}); 1445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return null; 1455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var signChallenges = request[signChallengesName]; 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var appId; 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (request['appId']) { 1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci appId = request['appId']; 1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else { 1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // A valid sign data has at least one challenge, so get the appId from 1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // the first challenge. 1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci appId = signChallenges[0]['appId']; 1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Sanity check 1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!appId) { 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci console.warn(UTIL_fmt('empty sign appId?')); 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorCb({errorCode: ErrorCodes.BAD_REQUEST}); 1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return null; 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var timer = createTimerForRequest( 1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) FACTORY_REGISTRY.getCountdownFactory(), request); 1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var logMsgUrl = request['logMsgUrl']; 1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Queue sign requests from the same origin, to protect against simultaneous 1675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // sign-out on many tabs resulting in repeated sign-in requests. 1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var queuedSignRequest = new QueuedSignRequest(signChallenges, 1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci timer, nonNullOrigin, errorCb, successCb, appId, sender.tlsChannelId, 1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) logMsgUrl); 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var requestToken = signRequestQueue.queueRequest(appId, nonNullOrigin, 1725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu queuedSignRequest.begin.bind(queuedSignRequest), timer); 1735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu queuedSignRequest.setToken(requestToken); 1745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return queuedSignRequest; 1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Returns whether the request appears to be a valid sign request. 1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The request. 1806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} signChallengesName The name of the sign challenges value in 1816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * the request. 1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {boolean} Whether the request appears valid. 1835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function isValidSignRequest(request, signChallengesName) { 1856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!request.hasOwnProperty(signChallengesName)) 1865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return false; 1876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var signChallenges = request[signChallengesName]; 1885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // If a sign request contains an empty array of challenges, it could never 1895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // be fulfilled. Fail. 1906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!signChallenges.length) 1915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return false; 1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var hasAppId = request.hasOwnProperty('appId'); 1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return isValidSignChallengeArray(signChallenges, !hasAppId); 1945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 1955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Adapter class representing a queued sign request. 1986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {!Array.<SignChallenge>} signChallenges The sign challenges. 199010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Countdown} timer Timeout timer 200010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} origin Signature origin 2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function(U2fError)} errorCb Error callback 202010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {function(SignChallenge, string, string)} successCb Success callback 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string|undefined} opt_appId The app id for the entire request. 204010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string|undefined} opt_tlsChannelId TLS Channel Id 205010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string|undefined} opt_logMsgUrl Url to post log messages to 2065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 2075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {Closeable} 2085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function QueuedSignRequest(signChallenges, timer, origin, errorCb, 2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci successCb, opt_appId, opt_tlsChannelId, opt_logMsgUrl) { 2116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** @private {!Array.<SignChallenge>} */ 2126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.signChallenges_ = signChallenges; 2135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Countdown} */ 2145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.timer_ = timer; 2155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string} */ 2165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.origin_ = origin; 2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** @private {function(U2fError)} */ 2185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.errorCb_ = errorCb; 2195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {function(SignChallenge, string, string)} */ 2205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.successCb_ = successCb; 2215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string|undefined} */ 2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.appId_ = opt_appId; 2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** @private {string|undefined} */ 2245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.tlsChannelId_ = opt_tlsChannelId; 2255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string|undefined} */ 2265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.logMsgUrl_ = opt_logMsgUrl; 2275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.begun_ = false; 2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 2305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.closed_ = false; 2315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 2325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Closes this sign request. */ 2345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.close = function() { 2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.closed_) return; 2365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.begun_ && this.signer_) { 2375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signer_.close(); 2385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.token_) { 2405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.token_.complete(); 2415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.closed_ = true; 2435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {QueuedRequestToken} token Token for this sign request. 2475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.setToken = function(token) { 2495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {QueuedRequestToken} */ 2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.token_ = token; 2515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this sign request may begin work. 2555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {QueuedRequestToken} token Token for this sign request. 2565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.begin = function(token) { 2585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.begun_ = true; 2595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.setToken(token); 2606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.signer_ = new Signer(this.timer_, this.origin_, 26146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) this.signerFailed_.bind(this), this.signerSucceeded_.bind(this), 2625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.tlsChannelId_, this.logMsgUrl_); 2631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!this.signer_.setChallenges(this.signChallenges_, this.appId_)) { 2645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu token.complete(); 2651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.errorCb_({errorCode: ErrorCodes.BAD_REQUEST}); 2665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this request's signer fails. 2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {U2fError} error The failure reported by the signer. 2725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciQueuedSignRequest.prototype.signerFailed_ = function(error) { 2755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.token_.complete(); 2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.errorCb_(error); 2775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this request's signer succeeds. 2815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {SignChallenge} challenge The challenge that was signed. 2825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} info The sign result. 283010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} browserData Browser data JSON 2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.signerSucceeded_ = 2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(challenge, info, browserData) { 2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.token_.complete(); 2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.successCb_(challenge, info, browserData); 2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates an object to track signing with a gnubby. 2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Countdown} timer Timer for sign request. 2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} origin The origin making the request. 2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function(U2fError)} errorCb Called when the sign operation fails. 2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(SignChallenge, string, string)} successCb Called when the 2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * sign operation succeeds. 2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_tlsChannelId the TLS channel ID, if any, of the origin 3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * making the request. 3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl The url to post log messages to. 3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function Signer(timer, origin, errorCb, successCb, 30546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) opt_tlsChannelId, opt_logMsgUrl) { 3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Countdown} */ 3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.timer_ = timer; 3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string} */ 3095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.origin_ = origin; 3101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** @private {function(U2fError)} */ 3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.errorCb_ = errorCb; 3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {function(SignChallenge, string, string)} */ 3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.successCb_ = successCb; 3145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string|undefined} */ 3155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.tlsChannelId_ = opt_tlsChannelId; 3165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string|undefined} */ 3175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.logMsgUrl_ = opt_logMsgUrl; 3185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.challengesSet_ = false; 3215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.done_ = false; 3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Object.<string, string>} */ 3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.browserData_ = {}; 3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Object.<string, SignChallenge>} */ 3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.serverChallenges_ = {}; 3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Allow http appIds for http origins. (Broken, but the caller deserves 3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // what they get.) 3305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 3315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.allowHttp_ = this.origin_ ? this.origin_.indexOf('http://') == 0 : false; 3326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** @private {Closeable} */ 3336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.handler_ = null; 3345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 3355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Sets the challenges to be signed. 3386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Array.<SignChallenge>} signChallenges The challenges to set. 3391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string=} opt_appId The app id for the entire request. 3405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} Whether the challenges could be set. 3415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciSigner.prototype.setChallenges = function(signChallenges, opt_appId) { 3435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.challengesSet_ || this.done_) 3445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return false; 3456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** @private {Array.<SignChallenge>} */ 3466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.signChallenges_ = signChallenges; 3471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** @private {string|undefined} */ 3481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.appId_ = opt_appId; 3495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 3505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.challengesSet_ = true; 3515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.checkAppIds_(); 35346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return true; 3545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 357116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Checks the app ids of incoming requests. 358116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @private 3595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 360116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.checkAppIds_ = function() { 3616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var appIds = getDistinctAppIds(this.signChallenges_); 3621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (this.appId_) { 3631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci appIds = UTIL_unionArrays([this.appId_], appIds); 3641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 365116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!appIds || !appIds.length) { 3661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var error = { 3671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorCode: ErrorCodes.BAD_REQUEST, 3681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorMessage: 'missing appId' 3691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci }; 3701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.notifyError_(error); 3716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 3726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) FACTORY_REGISTRY.getOriginChecker().canClaimAppIds(this.origin_, appIds) 3746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) .then(this.originChecked_.bind(this, appIds)); 3756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}; 3766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 3786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Called with the result of checking the origin. When the origin is allowed 3796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * to claim the app ids, begins checking whether the app ids also list the 3806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * origin. 3816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {!Array.<string>} appIds The app ids. 3826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {boolean} result Whether the origin could claim the app ids. 3836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @private 3846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 3856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)Signer.prototype.originChecked_ = function(appIds, result) { 3866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!result) { 3871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var error = { 3881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorCode: ErrorCodes.BAD_REQUEST, 3891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorMessage: 'bad appId' 3901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci }; 3911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.notifyError_(error); 392116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 3935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 394116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch /** @private {!AppIdChecker} */ 3956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.appIdChecker_ = new AppIdChecker(FACTORY_REGISTRY.getTextFetcher(), 3966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.timer_.clone(), this.origin_, 397116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch /** @type {!Array.<string>} */ (appIds), this.allowHttp_, 398116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.logMsgUrl_); 3996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.appIdChecker_.doCheck().then(this.appIdChecked_.bind(this)); 400116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}; 401116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 402116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/** 403116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Called with the result of checking app ids. When the app ids are valid, 404116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * adds the sign challenges to those being signed. 405116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {boolean} result Whether the app ids are valid. 406116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @private 407116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */ 408116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.appIdChecked_ = function(result) { 409116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!result) { 4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var error = { 4111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorCode: ErrorCodes.BAD_REQUEST, 4121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci errorMessage: 'bad appId' 4131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci }; 4141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.notifyError_(error); 415116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 416116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 417116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!this.doSign_()) { 4181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); 419116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 4205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 424116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Begins signing this signer's challenges. 425116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @return {boolean} Whether the challenge could be added. 4265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 4275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 428116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.doSign_ = function() { 4296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Create the browser data for each challenge. 4306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (var i = 0; i < this.signChallenges_.length; i++) { 4316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var challenge = this.signChallenges_[i]; 4326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var serverChallenge = challenge['challenge']; 4336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var keyHandle = challenge['keyHandle']; 4345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var browserData = 4365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu makeSignBrowserData(serverChallenge, this.origin_, this.tlsChannelId_); 4376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.browserData_[keyHandle] = browserData; 4386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.serverChallenges_[keyHandle] = challenge; 4396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 4405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var encodedChallenges = encodeSignChallenges(this.signChallenges_, 4421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.appId_, this.getChallengeHash_.bind(this)); 4435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 444116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var timeoutSeconds = this.timer_.millisecondsUntilExpired() / 1000.0; 445116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var request = makeSignHelperRequest(encodedChallenges, timeoutSeconds, 446116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.logMsgUrl_); 4476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.handler_ = 4486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) FACTORY_REGISTRY.getRequestHelper() 4496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) .getHandler(/** @type {HelperRequest} */ (request)); 4506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!this.handler_) 4516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return false; 4526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return this.handler_.run(this.helperComplete_.bind(this)); 4535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 4566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} keyHandle The key handle used with the challenge. 4576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} challenge The challenge. 4586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {string} The hashed challenge associated with the key 4596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * handle/challenge pair. 4605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 4615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 4626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)Signer.prototype.getChallengeHash_ = function(keyHandle, challenge) { 4636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return B64_encode(sha256HashOfString(this.browserData_[keyHandle])); 4645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Closes this signer. */ 4675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.close = function() { 468116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (this.appIdChecker_) { 469116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.appIdChecker_.close(); 470116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 4716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (this.handler_) { 4726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.handler_.close(); 4736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.handler_ = null; 474116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 4756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.timer_.clearTimeout(); 4765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 4791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Notifies the caller of error. 4801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {U2fError} error Error. 4815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 4825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 4831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciSigner.prototype.notifyError_ = function(error) { 4845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.done_) 4855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 4865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.close(); 4875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.done_ = true; 4881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.errorCb_(error); 4895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 4925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Notifies the caller of success. 4935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {SignChallenge} challenge The challenge that was signed. 4945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} info The sign result. 495010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} browserData Browser data JSON 4965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 4975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 4985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.notifySuccess_ = function(challenge, info, browserData) { 4995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.done_) 5005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 5015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.close(); 5025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.done_ = true; 5035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.successCb_(challenge, info, browserData); 5045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 5055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 5065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 507116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Called by the helper upon completion. 5086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {HelperReply} helperReply The result of the sign request. 509116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {string=} opt_source The source of the sign result. 5105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 5115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 5126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)Signer.prototype.helperComplete_ = function(helperReply, opt_source) { 5136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (helperReply.type != 'sign_helper_reply') { 5141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.notifyError_({errorCode: ErrorCodes.OTHER_ERROR}); 5156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 5166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 5176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var reply = /** @type {SignHelperReply} */ (helperReply); 5185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 519116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (reply.code) { 5201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var reportedError = mapDeviceStatusCodeToU2fError(reply.code); 521116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch console.log(UTIL_fmt('helper reported ' + reply.code.toString(16) + 5221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ', returning ' + reportedError.errorCode)); 523116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.notifyError_(reportedError); 524116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else { 525116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (this.logMsgUrl_ && opt_source) { 526116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var logMsg = 'signed&source=' + opt_source; 527116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch logMessage(logMsg, this.logMsgUrl_); 528116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 5295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 5306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var key = reply.responseData['keyHandle']; 531116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var browserData = this.browserData_[key]; 532116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Notify with server-provided challenge, not the encoded one: the 533116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // server-provided challenge contains additional fields it relies on. 534116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var serverChallenge = this.serverChallenges_[key]; 535116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.notifySuccess_(serverChallenge, reply.responseData.signatureData, 536116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch browserData); 53746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) } 5385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 539