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 Does common handling for requests coming from web pages and
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * routes them to the provided handler.
85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Gets the scheme + origin from a web url.
12010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} url Input url
13010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @return {?string} Scheme and origin part if url parses
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction getOriginFromUrl(url) {
165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var re = new RegExp('^(https?://)[^/]*/?');
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var originarray = re.exec(url);
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (originarray == null) return originarray;
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var origin = originarray[0];
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  while (origin.charAt(origin.length - 1) == '/') {
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    origin = origin.substring(0, origin.length - 1);
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (origin == 'http:' || origin == 'https:')
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return null;
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return origin;
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Returns whether the registered key appears to be valid.
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} registeredKey The registered key object.
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {boolean} appIdRequired Whether the appId property is required on
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *     each challenge.
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {boolean} Whether the object appears valid.
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifunction isValidRegisteredKey(registeredKey, appIdRequired) {
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (appIdRequired && !registeredKey.hasOwnProperty('appId')) {
371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!registeredKey.hasOwnProperty('keyHandle'))
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (registeredKey['version']) {
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (registeredKey['version'] != 'U2F_V1' &&
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        registeredKey['version'] != 'U2F_V2') {
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return true;
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/**
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Returns whether the array of registered keys appears to be valid.
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Array.<Object>} registeredKeys The array of registered keys.
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {boolean} appIdRequired Whether the appId property is required on
541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *     each challenge.
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {boolean} Whether the array appears valid.
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifunction isValidRegisteredKeyArray(registeredKeys, appIdRequired) {
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return registeredKeys.every(function(key) {
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return isValidRegisteredKey(key, appIdRequired);
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  });
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/**
646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Returns whether the array of SignChallenges appears to be valid.
656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Array.<SignChallenge>} signChallenges The array of sign challenges.
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {boolean} appIdRequired Whether the appId property is required on
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *     each challenge.
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {boolean} Whether the array appears valid.
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifunction isValidSignChallengeArray(signChallenges, appIdRequired) {
716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  for (var i = 0; i < signChallenges.length; i++) {
726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    var incomingChallenge = signChallenges[i];
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (!incomingChallenge.hasOwnProperty('challenge'))
745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return false;
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!isValidRegisteredKey(incomingChallenge, appIdRequired)) {
765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return false;
775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return true;
805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Posts the log message to the log url.
835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} logMsg the log message to post.
845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl the url to post log messages to.
855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction logMessage(logMsg, opt_logMsgUrl) {
875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  console.log(UTIL_fmt('logMessage("' + logMsg + '")'));
885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!opt_logMsgUrl) {
905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // Image fetching is not allowed per packaged app CSP.
935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // But video and audio is.
945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var audio = new Audio();
955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  audio.src = opt_logMsgUrl + logMsg;
965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} request Request object
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {MessageSender} sender Sender frame
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Function} sendResponse Response callback
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {?Closeable} Optional handler object that should be closed when port
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *     closes
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */
1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifunction handleWebPageRequest(request, sender, sendResponse) {
1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  switch (request.type) {
1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case GnubbyMsgTypes.ENROLL_WEB_REQUEST:
1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return handleWebEnrollRequest(sender, request, sendResponse);
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case GnubbyMsgTypes.SIGN_WEB_REQUEST:
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return handleWebSignRequest(sender, request, sendResponse);
1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case MessageTypes.U2F_REGISTER_REQUEST:
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return handleU2fEnrollRequest(sender, request, sendResponse);
1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case MessageTypes.U2F_SIGN_REQUEST:
1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return handleU2fSignRequest(sender, request, sendResponse);
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    default:
1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      sendResponse(
1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          makeU2fErrorResponse(request, ErrorCodes.BAD_REQUEST, undefined,
1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci              MessageTypes.U2F_REGISTER_RESPONSE));
1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return null;
1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/**
1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Makes a response to a request.
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The request to make a response to.
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} responseSuffix How to name the response's type.
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string=} opt_defaultType The default response type, if none is
1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     present in the request.
1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The response object.
1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeResponseForRequest(request, responseSuffix, opt_defaultType) {
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var type;
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (request && request.type) {
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    type = request.type.replace(/_request$/, responseSuffix);
1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  } else {
1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    type = opt_defaultType;
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var reply = { 'type': type };
1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (request && request.requestId) {
1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    reply.requestId = request.requestId;
1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return reply;
1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Makes a response to a U2F request with an error code.
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The request to make a response to.
1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {ErrorCodes} code The error code to return.
1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string=} opt_detail An error detail string.
1546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string=} opt_defaultType The default response type, if none is
1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     present in the request.
1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The U2F error.
1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeU2fErrorResponse(request, code, opt_detail, opt_defaultType) {
1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var reply = makeResponseForRequest(request, '_response', opt_defaultType);
1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var error = {'errorCode': code};
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (opt_detail) {
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    error['errorMessage'] = opt_detail;
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  reply['responseData'] = error;
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return reply;
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Makes a success response to a web request with a responseData object.
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The request to make a response to.
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} responseData The response data.
1726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The web error.
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeU2fSuccessResponse(request, responseData) {
1756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var reply = makeResponseForRequest(request, '_response');
1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  reply['responseData'] = responseData;
1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return reply;
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
1816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Makes a response to a web request with an error code.
1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The request to make a response to.
1836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {GnubbyCodeTypes} code The error code to return.
1846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string=} opt_defaultType The default response type, if none is
1856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     present in the request.
1866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The web error.
1876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
1886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeWebErrorResponse(request, code, opt_defaultType) {
1896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var reply = makeResponseForRequest(request, '_reply', opt_defaultType);
1906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  reply['code'] = code;
1916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return reply;
1926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
1956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Makes a success response to a web request with a responseData object.
1966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} request The request to make a response to.
1976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object} responseData The response data.
1986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {Object} The web error.
1996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
2006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function makeWebSuccessResponse(request, responseData) {
2016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var reply = makeResponseForRequest(request, '_reply');
2026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  reply['code'] = GnubbyCodeTypes.OK;
2036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  reply['responseData'] = responseData;
2046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return reply;
2056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
2086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Maps an error code from the ErrorCodes namespace to the GnubbyCodeTypes
2096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * namespace.
2106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {ErrorCodes} errorCode Error in the ErrorCodes namespace.
2116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {boolean} forSign Whether the error is for a sign request.
2126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {GnubbyCodeTypes} Error code in the GnubbyCodeTypes namespace.
2136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
2146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function mapErrorCodeToGnubbyCodeType(errorCode, forSign) {
2156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var code;
2166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  switch (errorCode) {
2176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case ErrorCodes.BAD_REQUEST:
2186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return GnubbyCodeTypes.BAD_REQUEST;
2196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case ErrorCodes.DEVICE_INELIGIBLE:
2216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return forSign ? GnubbyCodeTypes.NONE_PLUGGED_ENROLLED :
2226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          GnubbyCodeTypes.ALREADY_ENROLLED;
2236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case ErrorCodes.TIMEOUT:
2256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return GnubbyCodeTypes.WAIT_TOUCH;
2266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return GnubbyCodeTypes.UNKNOWN_ERROR;
2286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Maps a helper's error code from the DeviceStatusCodes namespace to a
2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * U2fError.
2336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {number} code Error code from DeviceStatusCodes namespace.
2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {U2fError} An error.
2356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifunction mapDeviceStatusCodeToU2fError(code) {
2376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  switch (code) {
2386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case DeviceStatusCodes.WRONG_DATA_STATUS:
2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return {errorCode: ErrorCodes.DEVICE_INELIGIBLE};
2406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case DeviceStatusCodes.TIMEOUT_STATUS:
2426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case DeviceStatusCodes.WAIT_TOUCH_STATUS:
2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return {errorCode: ErrorCodes.TIMEOUT};
2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    default:
2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var reportedError = {
2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        errorCode: ErrorCodes.OTHER_ERROR,
2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        errorMessage: 'device status code: ' + code.toString(16)
2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      };
2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return reportedError;
2516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
2536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
2556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Sends a response, using the given sentinel to ensure at most one response is
2566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * sent. Also closes the closeable, if it's given.
2576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {boolean} sentResponse Whether a response has already been sent.
2586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {?Closeable} closeable A thing to close.
2596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {*} response The response to send.
2606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Function} sendResponse A function to send the response.
2616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
2626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function sendResponseOnce(sentResponse, closeable, response, sendResponse) {
2636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (closeable) {
2646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    closeable.close();
2656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!sentResponse) {
2676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    sentResponse = true;
2686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    try {
2696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      // If the page has gone away or the connection has otherwise gone,
2706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      // sendResponse fails.
2716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      sendResponse(response);
2726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    } catch (exception) {
2736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      console.warn('sendResponse failed: ' + exception);
2746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
2756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  } else {
2766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    console.warn(UTIL_fmt('Tried to reply more than once! Juan, FIX ME'));
2776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
281010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!string} string Input string
2825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {Array.<number>} SHA256 hash value of string.
2835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction sha256HashOfString(string) {
2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var s = new SHA256();
2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  s.update(UTIL_StringToBytes(string));
2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return s.digest();
2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Normalizes the TLS channel ID value:
2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 1. Converts semantically empty values (undefined, null, 0) to the empty
2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     string.
2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 2. Converts valid JSON strings to a JS object.
2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 3. Otherwise, returns the input value unmodified.
296010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Object|string|undefined} opt_tlsChannelId TLS Channel id
2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {Object|string} The normalized TLS channel ID value.
2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction tlsChannelIdValue(opt_tlsChannelId) {
3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!opt_tlsChannelId) {
3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Case 1: Always set some value for  TLS channel ID, even if it's the empty
3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // string: this browser definitely supports them.
3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return '';
3045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
3055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (typeof opt_tlsChannelId === 'string') {
3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    try {
3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      var obj = JSON.parse(opt_tlsChannelId);
3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      if (!obj) {
3095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Case 1: The string value 'null' parses as the Javascript object null,
3105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // so return an empty string: the browser definitely supports TLS
3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // channel id.
3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        return '';
3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      }
3145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // Case 2: return the value as a JS object.
3155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return /** @type {Object} */ (obj);
3165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    } catch (e) {
3175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      console.warn('Unparseable TLS channel ID value ' + opt_tlsChannelId);
3185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // Case 3: return the value unmodified.
3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
3215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return opt_tlsChannelId;
3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates a browser data object with the given values.
3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} type A string representing the "type" of this browser data
3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     object.
3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} serverChallenge The server's challenge, as a base64-
3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     encoded string.
3305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} origin The server's origin, as seen by the browser.
331010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Object|string|undefined} opt_tlsChannelId TLS Channel Id
3325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {string} A string representation of the browser data object.
3335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction makeBrowserData(type, serverChallenge, origin, opt_tlsChannelId) {
3355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var browserData = {
3365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    'typ' : type,
3375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    'challenge' : serverChallenge,
3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    'origin' : origin
3395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  };
3405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  browserData['cid_pubkey'] = tlsChannelIdValue(opt_tlsChannelId);
3415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return JSON.stringify(browserData);
3425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
3435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates a browser data object for an enroll request with the given values.
3465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} serverChallenge The server's challenge, as a base64-
3475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     encoded string.
3485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} origin The server's origin, as seen by the browser.
349010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Object|string|undefined} opt_tlsChannelId TLS Channel Id
3505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {string} A string representation of the browser data object.
3515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction makeEnrollBrowserData(serverChallenge, origin, opt_tlsChannelId) {
3535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return makeBrowserData(
3545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      'navigator.id.finishEnrollment', serverChallenge, origin,
3555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      opt_tlsChannelId);
3565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
3575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates a browser data object for a sign request with the given values.
3605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} serverChallenge The server's challenge, as a base64-
3615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     encoded string.
3625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!string} origin The server's origin, as seen by the browser.
363010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Object|string|undefined} opt_tlsChannelId TLS Channel Id
3645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {string} A string representation of the browser data object.
3655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction makeSignBrowserData(serverChallenge, origin, opt_tlsChannelId) {
3675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return makeBrowserData(
3685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      'navigator.id.getAssertion', serverChallenge, origin, opt_tlsChannelId);
3695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
3705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Encodes the sign data as an array of sign helper challenges.
3736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Array.<SignChallenge>} signChallenges The sign challenges to encode.
3741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string=} opt_defaultAppId The app id to use for each challenge, if
3751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *     the challenge contains none.
3766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {function(string, string): string=} opt_challengeHashFunction
3776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     A function that produces, from a key handle and a raw challenge, a hash
3786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     of the raw challenge. If none is provided, a default hash function is
3796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     used.
3806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {!Array.<SignHelperChallenge>} The sign challenges, encoded.
3815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifunction encodeSignChallenges(signChallenges, opt_defaultAppId,
3831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    opt_challengeHashFunction) {
3846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  function encodedSha256(keyHandle, challenge) {
3856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return B64_encode(sha256HashOfString(challenge));
3866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var challengeHashFn = opt_challengeHashFunction || encodedSha256;
3886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var encodedSignChallenges = [];
3896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (signChallenges) {
3906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    for (var i = 0; i < signChallenges.length; i++) {
3916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      var challenge = signChallenges[i];
3926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      var challengeHash =
3936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          challengeHashFn(challenge['keyHandle'], challenge['challenge']);
3941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      var appId;
3951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      if (challenge.hasOwnProperty('appId')) {
3961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        appId = challenge['appId'];
3971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      } else {
3981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        appId = opt_defaultAppId;
3991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      }
4006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      var encodedChallenge = {
4016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        'challengeHash': challengeHash,
4021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        'appIdHash': B64_encode(sha256HashOfString(appId)),
4036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        'keyHandle': challenge['keyHandle'],
4046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        'version': (challenge['version'] || 'U2F_V1')
4056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      };
4066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      encodedSignChallenges.push(encodedChallenge);
4076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
4086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
4096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return encodedSignChallenges;
4105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
411116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
412116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/**
413116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Makes a sign helper request from an array of challenges.
414116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {Array.<SignHelperChallenge>} challenges The sign challenges.
415116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {number=} opt_timeoutSeconds Timeout value.
416116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {string=} opt_logMsgUrl URL to log to.
417116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @return {SignHelperRequest} The sign helper request.
418116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */
419116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfunction makeSignHelperRequest(challenges, opt_timeoutSeconds, opt_logMsgUrl) {
420116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var request = {
421116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'type': 'sign_helper_request',
422116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'signData': challenges,
4236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    'timeout': opt_timeoutSeconds || 0,
4246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    'timeoutSeconds': opt_timeoutSeconds || 0
425116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  };
426116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (opt_logMsgUrl !== undefined) {
427116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    request.logMsgUrl = opt_logMsgUrl;
428116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
429116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return request;
430116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
431